当前位置:   article > 正文

数据结构——双向链表及其总结

数据结构——双向链表及其总结

1.概述

链表根据是否带头、是否双向、是否循环可以分为八种,双向链表是典型的带头双向循环链表。

双向链表的结构可以表示为下:

  1. struct ListNode
  2. {
  3. int data;
  4. struct ListNode* next;
  5. struct ListNode* prev;
  6. }

2.双向链表的实现过程及其解析

双向链表的实现依旧包含List.h,List.c,test.c

2.1 链表初始化

双向链表为空的情况:只有一个哨兵位。

先定义一个结构如下:

  1. typedef int LTDatatype;
  2. typedef struct ListNode {
  3. LTDatatype data;
  4. struct ListNode* next;
  5. struct ListNode* prev;
  6. }LTNode;

链表初始化时我们应该先创建一个哨兵位,则实现代码如下:

  1. LTNode* LTBuyNode(LTDatatype x)
  2. {
  3. LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
  4. if (newnode == NULL)
  5. {
  6. perror("malloc fail!");
  7. exit(1);
  8. }
  9. newnode->data = x;
  10. newnode->next = newnode;
  11. newnode->prev = newnode;
  12. return newnode;
  13. }
  14. //初始化
  15. void LTInit(LTNode** pphead)
  16. {
  17. //创建一个哨兵位
  18. *pphead = LTBuyNode(-1);
  19. }

2.2插入数据

2.2.1 尾插

 因为在双向链表中有哨兵位,所以在这种情况下不用二级指针,用一级指针就可以实现。

用一级还是二级,要看pphead指向的结点会不会改变,如果发生改变,传二级,如果不发生改变,要传一级。

尾插时发生改变的是头结点和最后一个结点。

第一个结点:第一个有效的结点。

哨兵位:头结点。

代码实现如下:

  1. //插入数据
  2. //尾插
  3. void LTPushBack(LTNode* phead, LTDatatype x)
  4. {
  5. assert(phead);
  6. LTNode* newnode = LTBuyNode(x);
  7. //从后往前
  8. newnode->next = phead;
  9. newnode->prev = phead->prev;
  10. phead->prev->next = newnode;
  11. phead->prev = newnode;
  12. }

2.2.2 头插

打印链表的代码:

  1. //打印链表
  2. void LTPrint(LTNode* phead)
  3. {
  4. LTNode* pcur = phead->next;
  5. while (pcur != phead)
  6. {
  7. printf("%d->", pcur->data);
  8. pcur = pcur->next;
  9. }
  10. printf("\n");
  11. }

头插的代码实现:

  1. //头插
  2. void LTPushFront(LTNode* phead, LTDatatype x)
  3. {
  4. assert(phead);
  5. //相当于插入哨兵位之后
  6. LTNode* newnode = LTBuyNode(x);
  7. newnode->next = phead->next;
  8. newnode->prev = phead;
  9. phead->next->prev = newnode;
  10. phead->next = newnode;
  11. }

 2.3 判断链表是否为空

  1. //判断链表是否为空
  2. bool LTEmpty(LTNode* phead)
  3. {
  4. assert(phead);
  5. return phead->next == phead;
  6. }

2.4 删除数据

2.4.1 尾删

  1. //删除数据
  2. //尾删
  3. void LTPopBack(LTNode* phead)
  4. {
  5. assert(phead);
  6. assert(!LTEmpty(phead));
  7. LTNode* del = phead->prev;
  8. LTNode* prev = del->prev;
  9. prev->next = phead;
  10. phead->prev = prev;
  11. free(del);
  12. del = NULL;
  13. }

 2.4.2 头删

  1. //头删
  2. void LTPopFront(LTNode* phead)
  3. {
  4. assert(phead);
  5. assert(!LTEmpty(phead));
  6. LTNode* del = phead->next;
  7. del->next->prev = phead;
  8. phead->next = del->next;
  9. free(del);
  10. del = NULL;
  11. }

2.5 查找 

  1. //查找
  2. LTNode* LTFind(LTNode* phead, LTDatatype x)
  3. {
  4. assert(phead);
  5. LTNode* pcur = phead->next;
  6. while (pcur != phead)
  7. {
  8. if (pcur->data == x)
  9. {
  10. return pcur;
  11. }
  12. pcur = pcur->next;
  13. }
  14. return NULL;
  15. }

 2.6 在指定位置之后插入数据

  1. //在指定位置之后插入数据
  2. void LTInsert(LTNode* pos, LTDatatype x)
  3. {
  4. assert(pos);
  5. LTNode* newnode = LTBuyNode(x);
  6. newnode->next = pos->next;
  7. newnode->prev = pos;
  8. pos->next->prev = newnode;
  9. pos->next = newnode;
  10. }

 2.7 删除指定位置数据

  1. //删除指定位置结点
  2. void LTErase(LTNode* pos)
  3. {
  4. assert(pos);
  5. pos->prev->next = pos->next;
  6. pos->next->prev = pos->prev;
  7. free(pos);
  8. pos = NULL;
  9. }

 2.8 销毁链表

  1. //销毁链表
  2. void LTDestroy(LTNode** pphead)
  3. {
  4. assert(pphead && *pphead);
  5. LTNode* pcur = (*pphead)->next;
  6. while (pcur != pphead)
  7. {
  8. LTNode* Next = pcur->next;
  9. free(pcur);
  10. pcur = Next;
  11. }
  12. pcur = NULL;
  13. free(*pphead);
  14. *pphead = NULL;
  15. }

2.9 代码改善 

在我们写代码时我们总是要考虑传二级指针,还是一级指针,那么有没有什么方法可以进行改进呢?

2.9.1 改进销毁链表的代码 

  1. void LTDestroy1(LTNode* phead)
  2. {
  3. assert(phead);
  4. LTNode* pcur = phead->next;
  5. while (pcur != phead)
  6. {
  7. LTNode* Next = pcur->next;
  8. free(pcur);
  9. pcur = Next;
  10. }
  11. free(phead);
  12. phead = NULL;
  13. pcur = NULL;
  14. }

 传一级时,需要手动将plist置为NULL。

2.9.2  改进初始化的链表

  1. LTNode* LTInit1()
  2. {
  3. LTNode* phead = LTBuyNode(-1);
  4. return phead;
  5. }

在调用时应该用:

  1. //调用时为
  2. LTNode* plist = LTInit1();

 

 3.总结

(该图片来自比特就业课的课件) 

在图中我们可以分别出顺序表和链表之间的关系,我们要重点关注其应用场景。

今天就到这里,我们下一个知识点见(* ̄︶ ̄)!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/908852
推荐阅读
相关标签
  

闽ICP备14008679号