链表2(数据结构与算法基础)

目录

循环链表,双向链表

一、循环链表

带尾指针循环链表的合并

二、双向链表

双向链表的定义

双向链表的插入

双向链表的删除

单、双、循环链表时间效率比较


循环链表,双向链表

一、循环链表

  • 是一种头尾相接的链表(即表中最后一个结点的指针域指向头结点,整个链表形成一个环)。

  • 当循环单链表为空表时,头结点的指针域指向头结点。

  • 优点

    • 从表中任一结点出发均可找到表中其他结点。

  • 由于循环链表中没有NULL指针,故涉及遍历操作时,其终止条件不再像非循环链表那样 判断 p 或 p->next是否为空 ,而是 判断它们是否等于头指针(p != L 或 p->next != L)

  • 表的操作通常时在表的 首尾位置 上进行。

带尾指针循环链表的合并

将Tb合并在Ta之后

思路:

  1. p存表头结点:p = Ta->next;

  2. Tb表头连接到Ta表尾:Ta->next = Tb->next->next;

  3. 释放Tb表头结点:delete Tb->next;

  4. 修改指针:Tb->next = p;

算法描述:

 LinkList Connect(LinkList Ta, LinkList Tb) {    //假设Ta、Tb都是非空的单循环链表
     p = Ta->next;                           //p存Ta表头结点
     Ta->next = Tb->next->next;              //Tb表头连接Ta表尾
     delete Tb->next;                        //释放Tb表头结点
     Tb->next = p;                           //修改指针
     return Tb;
 }
  • 时间复杂度 O(1)。

二、双向链表

单链表在查找某个结点的前驱结点时令人头痛,因为它每个结点只有一个指向后继结点的指针(next)。双向链表就不同了,双向链表不仅和单链表一样有一个指向后继结点的指针(next),还比单链表多了一个指向前驱结点的指针(prior)。

双向链表的定义

 typedef struct DuLnode {
     ElemType data;
     struct DuLnode *prior, *next;
 } DuLnode, *DuLinkList;

双向链表为空表时,prior和next都指向NULL。

  • of course,当尾结点的next域指向头结点,头结点的prior域指向尾结点时,就成了 双向循环链表

双向循环链表为空表时,prior和next都指向头结点。

  • 双向链表结构的 对称性 (设指针p指向某一结点):

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

在双向链表中有些操作(如ListLength、GetElem等),只需要操作一个方向上的指针就可以完成,和单链表一样。但在插入、删除时,则需要同时修改两个方向上的指针,两者的操作的时间复杂度均为O(n)。

双向链表的插入

思路:

  1. s->next = p->prior;

  2. p->prior->next = s;

  3. s->next = p;

  4. p->prior = s;

    第4步一定要在第1、2步之后,其他顺序可以交换!!

算法描述:

 Status ListInsert_DuL(DuLinkList &L, int i, ElemType e) {
     //在带头结点的双向链表L中第i个位置之前插入元素e
     if(!(p = GetElemP_DuL(L,i))) return ERROR;
     s = new DuLNode;
     s->data = e;            
     s->prior = p->prior;
     p->prior->next = s;
     s->next = p;
     p->prior = s;
     return OK;
 }

就插入操作而言 时间复杂度为 O(1),但若是要加上查找,时间复杂度就为 O(n)。

双向链表的删除

只需要两步,把要删除的结点的前驱和后继连起来,再把要删除的结点释放掉就可以了。

思路:

若指针p指向要删除的结点

  1. p->prior->next = p->next;

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

  2. delete p;

算法描述:

 Status ListDelete_DuL(DuLink &L, int i, ElemType &e) {
     //删除带头结点的双向循环链表L的第i个元素,并用e返回(也可以不返回)。
     if(!(p = GetElemP_DuL(L,i))) return ERROR;
     e = p->data;
     p->prior->next = p->next;
     p->next->prior = p->prior;
     free(p);
     return OK;
 }

就删除操作而言 时间复杂度为 O(1),但若是要加上查找,时间复杂度就为 O(n)。

单、双、循环链表时间效率比较

查找表头结点(首元结点)查找表尾结点查找结点*p的前驱结点
带头结点的单链表LL->next 时间复杂度O(1)从L->next依次向后遍历 时间复杂度O(n)通过p->next无法找到其前驱
带头结点仅设头指针L的循环单链表L->next 时间复杂度O(1)从L->next依次向后遍历 时间复杂度O(n)通过p->next可以找到其前驱 时间复杂度O(n)
带头结点仅设尾指针R的循环单链表R->next->next 时间复杂度O(1)R 时间复杂度O(1)通过p->next可以找到其前驱 时间复杂度O(n)
带头结点的双向循环链表LL->next 时间复杂度O(1)L->prior 时间复杂度O(1)p->prior 时间复杂度O(1)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值