【数据结构】循环链表API及实现(一)

简介: 【数据结构】循环链表API及实现

数据类型及API声明

循环链表是指首尾相连的链表,尾部元素指向头部元素,本文使用的模型是尾部元素指向0位置元素,而不是指向链表头。

1. //链表句柄
2. typedef void CircleList;
3. 
4. //循环链表的结点
5. typedef struct CircleListNode CircleListNode;
6. 
7. //循环链表的头结点
8. typedef struct CircleListHead CircleListHead;
9. 
10. //创建一个循环链表
11. CircleList* CircleList_Create();
12. 
13. //销毁一个循环链表
14. void List_Destroy(CircleList* list);
15. 
16. //清空一个循环链表
17. void CircleList_Clear(CircleList* list);
18. 
19. //返回循环链表的长度
20. int CircleList_Length(CircleList* list);
21. 
22. //插入一个元素
23. int CircleList_Insert(CircleList* list, CircleListNode* node, int pos);
24. 
25. //返回一个元素
26. CircleListNode* CircleList_Get(CircleList* list, int pos);
27. 
28. //按位置删除一个元素
29. CircleListNode* CircleList_Delete(CircleList* list, int pos);
30. 
31. //按结点值删除一个元素
32. CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);
33. 
34. //重置游标指向第一个结点元素
35. CircleListNode* CircleList_Reset(CircleList* list);
36. 
37. //返回当前游标
38. CircleListNode* CircleList_Current(CircleList* list);
39. 
40. //游标下移
41. CircleListNode* CircleList_Next(CircleList* list);

循环链表的API实现

循环链表结点结构体

链表结点包含了指向下一个结点的指针。

1. struct CircleListNode
2. {
3.  CircleListNode* next;
4. };

表头结构体

表头包含一个链表结构体head,用于指向链表的0号结点,包含一个游标slider,用于指示当前结点,包含一个长度length,用于指示链表元素个数

1. struct CircleListHead
2. {
3.  CircleListNode  head;
4.  CircleListNode* slider;
5.  int       length;
6. };

创建循环链表并返回循环链表句柄

  • 函数功能:创建一个循环链表并返回循环链表句柄,其实就是创建一个表头
  • 函数参数:无,链式结构不需要提前分配内存,而线性表的顺序结构需要提前分配内存
  • 函数返回:表头
1. CircleList* CircleList_Create()
2. {
3.  CircleListHead* pHead = NULL;
4.  pHead = (CircleListHead*)malloc(sizeof(CircleListHead));
5.  if (pHead == NULL)
6.  {
7.    printf("head = (CircleListHead*)malloc(sizeof(CircleListHead)) err\n");
8.    return NULL;
9.  }
10.   memset(pHead, 0, sizeof(CircleListHead));
11.   return pHead;
12. }

销毁循环链表

  • 函数功能:销毁循环链表就是释放表头资源,销毁循环链表前需要逐个删除链表结点
  • 函数参数:循环链表的句柄
  • 函数返回:无
1. void CircleList_Destroy(CircleList* list)
2. {
3.  CircleListHead* pHead = NULL;
4.  if (list == NULL)
5.  {
6.    printf("list == NULL\n");
7.    return;
8.  }
9.  pHead = (CircleListHead*)list;
10.   free(pHead);
11. }

清空循环链表

  • 函数功能:返回到循环链表刚创建的状态,即表头指针域指向NULL,长度置为0
  • 函数参数:循环链表的句柄
  • 函数返回:无
1. void CircleList_Clear(CircleList* list)
2. {
3.  CircleListHead* pHead = NULL;
4.  if (list == NULL)
5.  {
6.    printf("list == NULL\n");
7.    return;
8.  }
9.  pHead = (CircleListHead*)list;
10.   pHead->head.next = NULL;
11.   pHead->slider = NULL;
12.   pHead->length = 0;
13. }

返回循环链表元素个数

  • 函数功能:获取循环链表长度,即结点个数
  • 函数参数:循环链表句柄
  • 函数返回:节点个数
1. int CircleList_Length(CircleList* list)
2. {
3.  CircleListHead* pHead = NULL;
4.  if (list == NULL)
5.  {
6.    printf("list == NULL\n");
7.    return -1;
8.  }
9.  pHead = (CircleListHead*)list;
10.   return pHead->length;
11. }

插入元素

  • 函数功能:在循环链表中插入一个元素,被插入的元素已经在主调函数分配好内存,只需把链表结点CircleListNode串接起来即可
  • 函数参数:list 循环链表句柄 node 待插入元素 pos 插入位置
  • 函数返回:成功返回0
1. int CircleList_Insert(CircleList* list, CircleListNode* node, int pos)
2. {
3.  int i = 0;
4.  CircleListHead* pHead = NULL;
5.  CircleListNode* pCurrent = NULL;
6.  if ((list == NULL) || (node == NULL) || (pos < 0))
7.  {
8.    printf("err: (list == NULL) || (node == NULL) || (pos < 0)\n");
9.    return -1;
10.   }
11.   pHead = (CircleListHead*)list;
12.   pCurrent = &(pHead->head);
13.   for (i = 0; i < pos; i++)
14.   {
15.     pCurrent = pCurrent->next;
16.   }
17.   node->next = pCurrent->next;
18.   pCurrent->next = node;
19.   //第一次插入要设置游标位置
20.   if (CircleList_Length(list) == 0)
21.   {
22.     pHead->slider = node;
23.   }
24.   pHead->length++; //必须要先加1在判断是不是头插法,
25.            //因为插入后长度就变了,如果函数在最后加1,那么 pHead->length-1就不是尾部元素了
26.            //那样的话CircleList_Get(list, pHead->length - 1)获取的是尾部元素的上一个元素
27.   //如果是在0号位置插入(即头插法),pos为0,pCurrent不会移动
28.   if (pCurrent == (&(pHead->head)))
29.   {
30.     //获取尾部元素
31.     CircleListNode* pLast = CircleList_Get(list, pHead->length - 1);
32.     //node->next = pCurrent->next;
33.     //pCurrent->next = node;
34.     //形成环
35.     pLast->next = node;
36.   }
37.   return 0;
38. }

插入元素示意图

(1)普通位置插入

(2)尾部插入

(3)首次插入

(4)头部插入

根据上面的四种图片分析,前三种情况下,插入和普通链表的插入一样,借助一个辅助指针变量pCurrent,pCurrent为当前指针位置,初始状态他应该指向表头,当我们需要在pos号位置插入元素时,就将pCurrent后移pos位,比如要在2号位置插入,将pCurrent后移2次,那么pCurrent将指在1号结点处,而1号结点的next域指向2号结点,这时只需要把待插入结点插在pCurrent后面即可完成插入。但是第四种情况,也就是在0号位置插入时(头插法),因为循环链表要形成一个环,所以此时应该获取尾部结点,把尾部结点的指针域指向头部元素,也就是新插入的node元素,来形成一个循环。

返回指定位置元素

  • 函数功能:获取一个指定位置的元素
  • 函数参数:list 循环链表句柄 pos 要获取的元素标号
  • 函数返回:获取的结点元素
1. CircleListNode* CircleList_Get(CircleList* list, int pos)
2. {
3.  int i = 0;
4.  CircleListHead* pHead = NULL;
5.  CircleListNode* pCurrent = NULL;
6.  if ((list == NULL) || (pos < 0))
7.  {
8.    printf("err: (list == NULL) || (pos < 0)\n");
9.    return NULL;
10.   }
11.   pHead = (CircleListHead*)list;
12.   pCurrent = &(pHead->head);
13.   for (i = 0; i < pos; i++)
14.   {
15.     pCurrent = pCurrent->next;
16.   }
17.   return pCurrent->next;
18. }



相关文章
|
5天前
|
存储 Java
Java数据结构:链表
Java数据结构:链表
17 2
TU^
|
9天前
|
存储 索引
数据结构~~链表
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
TU^
19 0
|
4天前
|
缓存 算法
【数据结构】----链表--双向链表
【数据结构】----链表--双向链表
10 0
|
4天前
|
存储 缓存 算法
【数据结构】线性表----链表详解
【数据结构】线性表----链表详解
10 0
|
5天前
|
存储
数据结构——链表
数据结构——链表
12 0
|
5天前
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点.
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点
14 1
|
5天前
|
Java
DAY-1 | Java数据结构之链表:删除无头单链表中等于给定值 val 的所有节点
力扣203题解:使用时间复杂度为O(n)的思路删除链表中所有值为key的元素。引入辅助指针pre,记录cur的前一个节点,遍历链表时,若cur.val!=key,pre和cur同时前进;若cur.val==key,则pre.next=cur.next,cur继续前进,确保pre不急于跟随以处理连续相同值的情况。遍历结束后,处理头节点可能需要删除的特殊情况。
17 0
|
9天前
|
存储 缓存
数据结构(链表)
数据结构(链表)
17 0
|
10天前
|
算法 测试技术
【数据结构与算法 | 基础篇】单向循环链表实现队列
【数据结构与算法 | 基础篇】单向循环链表实现队列
|
10天前
|
算法 索引
【数据结构与算法 | 基础篇】[链表专题]力扣141, 142
【数据结构与算法 | 基础篇】[链表专题]力扣141, 142