【数据结构】-循环链表(双链表)

小鱼儿 2023-02-27 15:53 135阅读 0赞

循环双链表-带头结点

  • 1.头文件及类型定义
  • 2.循环双链表结点类型定义
  • 3.函数声明
  • 4.基本操作
    • 4.1 初始化循环双链表
    • 4.2 判空
    • 4.3 判断表尾结点
    • 4.4 按位查找
    • 4.5 指定结点后插
    • 4.6 删除指定结点后继结点
    • 4.7 创建循环双链表
    • 4.8 遍历
    • 4.9 销毁循环双链表
    • 4.10 main函数
  • 5.小结

1.头文件及类型定义

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #define ElemType int

2.循环双链表结点类型定义

  1. typedef struct DNode {
  2. //定义双链表结点类型
  3. ElemType data;
  4. struct DNode* prior, * next; //前驱和后继指针
  5. }DNode, * DLinkList;

3.函数声明

  1. /*函数声明*/
  2. bool InitDLinkList(DLinkList& L); //1.初始化循环双链表
  3. bool Empty(DLinkList L); //2.判空
  4. bool isTail(DLinkList L, DNode* p); //3.判断表尾结点
  5. DNode* GetElem(DLinkList L, int i); //4.按位查找
  6. bool InsertNextDNode(DNode* p, ElemType e); //5.指定结点后插
  7. bool DeleteNextDNode(DNode* p); //6.删除指定结点的后继结点
  8. DLinkList List_HeadInsert(DLinkList& L); //7.头插法
  9. void PrintDLinkList(DLinkList L); //8.遍历
  10. void DestoryList(DLinkList& L); //9.销毁循环双链表

4.基本操作

4.1 初始化循环双链表

  1. //1.初始化循环双链表(带头结点)
  2. bool InitDLinkList(DLinkList& L) {
  3. L = (DNode*)malloc(sizeof(DNode)); //创建头结点
  4. if (L == NULL)
  5. return false; //内存不足,分配失败
  6. L->prior = L; //头结点的prior指向L
  7. L->next = L; //头结点的next指向L
  8. return true;
  9. }

4.2 判空

  1. //2.判空(与循环单链表一致)
  2. bool Empty(DLinkList L) {
  3. if (L->next == L)
  4. return true; //表为空
  5. else
  6. return false; //表非空
  7. }

4.3 判断表尾结点

  1. //3.判断结点p是否为循环双链表的表尾结点(与循环单链表一致)
  2. bool isTail(DLinkList L, DNode* p) {
  3. if (p->next == L)
  4. return true;
  5. else
  6. return false;
  7. }

4.4 按位查找

  1. //4.查找操作-按位查找:返回第i个结点
  2. DNode* GetElem(DLinkList L, int i) {
  3. if (i < 0)
  4. return NULL;
  5. int j = 0;
  6. DNode* p = L;
  7. while (p != NULL && j < i) {
  8. p = p->next;
  9. j++;
  10. }
  11. return p; //也可能返回NULL,当输入i大于双链表长度时
  12. }

4.5 指定结点后插

  1. //5.插入操作-指定结点后插
  2. bool InsertNextDNode(DNode* p, ElemType e) {
  3. if (p == NULL) //非法参数
  4. return false;
  5. DNode* q = p->next; //q为p原先的后继结点
  6. DNode* s = (DNode*)malloc(sizeof(DNode)); //s为p新的后继结点
  7. if (s == NULL)
  8. return false; //内存不足,分配失败
  9. s->data = e; //为新结点赋值
  10. s->next = q;
  11. /*此处与双链表有区别,不需要判断,因为在循环双链表中每个结点都有后继结点,
  12. 因此不存在q为NULL的情况,故可以给q的前驱结点赋值,不会发生空指针的情况*/
  13. q->prior = s;
  14. s->prior = p;
  15. p->next = s;
  16. return true;
  17. }

4.6 删除指定结点后继结点

  1. //6.删除操作-删除指定结点p的后继结点q
  2. bool DeleteNextDNode(DNode* p) {
  3. if (p == NULL)
  4. return false; //非法参数:p不存在
  5. DNode* q = p->next;
  6. if (q == NULL)
  7. return false; //p没有后继结点
  8. p->next = q->next;
  9. q->next->prior = p; //此处与上述插入操作类似,也不需要判断
  10. free(q);
  11. return true;
  12. }

4.7 创建循环双链表

  1. //7.创建循环双链表:头插法
  2. DLinkList List_HeadInsert(DLinkList& L) {
  3. if (InitDLinkList(L)) //初始化循环双链表
  4. printf("循环双链表初始化成功!\n");
  5. else
  6. printf("循环双链表初始化失败!\n");
  7. if (Empty(L))
  8. printf("当前表为空!\n");
  9. else
  10. printf("当前表非空!\n");
  11. ElemType x;
  12. int i = 1; //用作记录插入第几个元素
  13. printf("开始创建循环双链表!\n请输入%d个元素:", i);
  14. scanf("%d", &x);
  15. while (x != 0) {
  16. if (InsertNextDNode(L, x)) //头插法本质是头结点的后插操作
  17. printf("成功插入第%d个元素:%d\n", i, x);
  18. else
  19. printf("插入第%d个元素失败!\n", i);
  20. printf("请输入%d个元素:", ++i);
  21. scanf("%d", &x);
  22. }
  23. printf("循环双链表创建完成!\n");
  24. return L;
  25. }

4.8 遍历

  1. //8.遍历循环双链表
  2. void PrintDLinkList(DLinkList L) {
  3. DNode* p;
  4. p = L->next;
  5. printf("遍历循环双链表:\n");
  6. while (p != L) {
  7. //此处判断与普通双链表有区别
  8. printf("%d\t", p->data);
  9. p = p->next;
  10. }
  11. printf("\n");
  12. }

4.9 销毁循环双链表

  1. //9.销毁操作
  2. void DestoryList(DLinkList& L) {
  3. printf("开始销毁双链表!\n");
  4. //循环释放各个数据结点--对头结点依次做删除操作
  5. while (L->next != L) //此处与普通双链表有区别
  6. DeleteNextDNode(L);
  7. free(L); //释放头结点
  8. L = NULL; //头指针指向NULL
  9. }

4.10 main函数

  1. int main() {
  2. DLinkList L;
  3. int i;
  4. /*以下操作只要涉及对循环双链表的改动,均遍历循环双链表*/
  5. /*1、头插法建立循环双链表*/
  6. L = List_HeadInsert(L);
  7. /*2、判空*/
  8. if (Empty(L))
  9. printf("当前表为空!\n");
  10. else
  11. printf("当前表非空!\n");
  12. PrintDLinkList(L);
  13. /*3、按位查找*/
  14. printf("请输入您要查找的位序i:");
  15. scanf("%d", &i);
  16. DNode* p = GetElem(L, i);
  17. printf("位序为%d的值为:%d\n", i, p->data);
  18. /*4.判断表尾结点*/
  19. if (isTail(L, p))
  20. printf("当前结点为表尾结点!\n");
  21. else
  22. printf("当前结点不是表尾结点!\n");
  23. //*5、指定结点后插,以按位查找的p结点为例*/
  24. ElemType e;
  25. printf("请输入您要后插的值:");
  26. scanf("%d", &e);
  27. if (InsertNextDNode(p, e))
  28. printf("p结点后插成功,元素值为:%d\n", e);
  29. else
  30. printf("p结点后插失败!\n");
  31. PrintDLinkList(L);
  32. /*6.再次判断表尾结点*/
  33. if (isTail(L, p))
  34. printf("当前结点为表尾结点!\n");
  35. else
  36. printf("当前结点不是表尾结点!\n");
  37. /*7、删除指定结点的后继结点,此处删除p的后继结点*/
  38. ElemType e1 = p->next->data;
  39. if (DeleteNextDNode(p))
  40. printf("此结点后继结点删除成功,值为:%d\n", e1);
  41. else
  42. printf("此结点后继结点删除失败!");
  43. PrintDLinkList(L);
  44. /*8、销毁循环双链表*/
  45. DestoryList(L);
  46. if (L == NULL)
  47. printf("循环双链表已销毁!\n");
  48. else
  49. printf("循环双链表销毁失败!\n");
  50. return 0;
  51. }

5.小结

  1. 普通双链表和循环双链表的对比
    (1)不同点
    初始化判空判断表尾结点遍历结束条件方面有微小的变化外,在循环双链表的销毁操作上也有微小变化,也是循环的结束条件发生了变化。另外,在循环双链表中的后插操作和指定结点的后继结点的删除操作不再需要判断插入结点或被删除结点是否是最后一个结点,因为所有结点都有后继结点。
    (2)相同点
    增删查以及创建操作没有改变。
  2. 说明
    上述代码主要是对循环双链表中与普通双链表有区别的操作进行了测试,其他基本操作与普通双链表基本一致。

发表评论

表情:
评论列表 (有 0 条评论,135人围观)

还没有评论,来说两句吧...

相关阅读