考试学习中心网

咨询投诉0931-8254357
主办单位:元海德教育宗旨:富家 兴教
0931-8254357

当前位置:主页 > 学习中心新 > 第二学位 >

第三节 - 线性表的链式存储结构(四)

发布时间:2020-09-20 10:34来源:未知

第三节 线性表的链式存储结构(四)

三、循环链表

循环链表与单链表的区别仅仅在于其尾结点的链域值不是NULL,而是一个指向头结点的指针。

循环链表的主要优点是:从表中任意一结点出发都能通过后移操作而遍历整个循环链表。

在循环链表中,将头指针改设为尾指针(rear)后,无论是找头结点、开始结点还是终端点都很方便,它们的存储位置分别是rear->next、rear->next->next和rear,这样可以简化某些操作。

例:已知有一个结点数据域为整型的,且按从大到小顺序排列的头结点指针为L的非空单循环链表,试写一算法插入一个结点(其数据域为x)至循环链表的适当位置,使之保持链表的有序性。

分析:要解决这个算法问题,首先就是要解决查找插入位置,方法:使用指针q扫描循环链表,循环条件应该是(q->data>x&&q!=L),同时使用指针p记录q的直接前驱,以方便进行插入操作。

算法描述

voidInsertList(LinkList L,int x)

{ //将值为x的新结点插入到有序循环链表中适当的位置

  ListNode *s,*p,*q;

  s=(ListNode *)malloc(sizeof(ListNode));        //申请结点存储空间

  s->data=x; p=L;

  q=p->next;                    //q指向开始结点

  while(q->data>x&&q!=L)              //查找插入位置

   { p=p->next;                  //p指向q的前趋结点

    q=p->next;                  //q指向当前结点

   }

  p->next=s;                   //插入*s结点

  s->next=q;

}

算法分析:

①该算法在最好情况下,也就是插入在第一个结点前面,循环一次也不执行;

②在最坏情况下,即插在最后一个结点之后,当循环需要执行n次;

③该算法的时间复杂度为O (n)。

真题选解

(例题·单选题)1、对于只在表的首、尾两端进行插入操作的线性表,宜采用的存储结构为(  )。

A.顺序表

B.用头指针表示的单循环链表

C.用尾指针表示的单循环链表

D.单链表

(例题·算法设计题)2、假设以带头结点的单循环链表作非递减有序线性表的存储结构。请设计一个时间复杂度为O(n)的算法,删除表中所有数值相同的多余元素,并释放结点空间。例如:

(7, 10, 10, 21, 30,42, 42,42, 5l, 70)

经算法操作后变为:

(7, 10, 21, 30,42, 5l, 70)

四、双向链表

双向循环链表示意图

双向链表结点

双向链表及其结点的类型描述

typedef struct dlnode

{ DataType data;

 struct  dlnode  *prior,*next;

}DLNode; //双向链表结点的类型定义

typedef  DLNode  *DLinkList;

DlinkList head; //head为指向双向链表的指针

双链表结构是一种对称结构,既有前向链,又有后向链,这就使得插入操作及删除操作都非常方便。设指针p指向双链表的某一结点,则双链表结构的对称性可用下式来描述:

p->prior ->next==p->next->prior==p

在双链表上实现求表长、按序号查找、定位、插入和删除等运算与单链表上的实现方法基本相同,不同的主要是插入和删除操作。

1、删除操作。

删除双链表中p所指结点

分析:操作过程如图所示

① p ->prior ->next=p->next;② p ->next ->prior=p->prior;

算法描述

DataType DLDelete(DLNode *p)

{ //删除带头结点的双向链表中指定结点*p

 p->prior->next=p->next;

 p->next->prior=p->prior; //上面两条语句的顺序可以调换,不影响操作结果。

 x=p->data;

 free(p);

 return x;

}

算法分析:算法的时间复杂度为O(1)

2、插入操作:

将值为x的新结点插入到带头结点的双向链表中指定结点*p之前。

分析:操作过程如图所示

算法描述

void DLInsert(DLNode *p,DataType x)

{ //将值为x的新结点插入到带头结点的双向链表中指定结点*p之前

 DLNode *s=(DLNode*)malloc(sizeof(DLNode));   //申请新结点

 s->data=x;

 s->prior=p->prior;               //完成①步操作

 s->next=p;                   //完成②步操作

 p->prior->next=s;                //完成③步操作

 p->prior=s;                  //完成④步操作

}

算法分析:算法的时间复杂度为O(1)

补充:在p所指结点的后面链入一个新结点*s,则需修改四个指针:

① s->prior= p; ② s->next=p->next;

③ p->next->prior=s; ④ p->next=s;

注:③和④两条语句的顺序不能调换,否则操作结果错误。

下面四条语句也可以完成插入操作。

① s->prior=p; ② s->next=p->next;

③ p->next=s; ④ s->next->prior=s;

真题选解

(例题·单选题)1、在带头结点的双向循环链表中插入一个新结点,需要修改的指针域数量是( )

A.2个    B.3个    C.4个    D.6个

例:假设有一个头结点指针为head的循环双向链表,其结点类型结构包括三个域:prior、data和next。其中data为数据域,next为指针域,指向其后继结点,prior也为指针域,其值为空(NULL),因此该双向链表其实是一个单循环链表。试写一算法,将其表修改为真正的双向循环链表。

分析:链表初始状态如下图所示

修改后的链表如下图所示

方法:以p指向头结点开始,循环使用语句p->next->prior=p;和p=p->next;即可实现题目要求。

算法描述

void trans(DLinkList head)

{

 DLNode *p;

 p=head; //使p指向头结点

 while(p->next!=head)      //依次从左向右,对每个结点的prior赋值

   { p->next->prior=p;     //p所指结点的直接后继结点的前趋就是p

   p=p->next;       //p指针指向下一个结点

  }

 head->prior=p;       // head的前趋指向表的终端结点

算法分析:算法要从头至尾扫描两个链表,算法的时间复杂度是O(n)


免费咨询

  • 甘肃: QQ
  • 四川: QQ
  • 山西: QQ
  • 陕西: QQ
  • 0931-8254357