目录
单向列表:每一个结点结构中只保存下一结点的地址,所以很难从后一结点找到前一节点;
双向列表:每一个结点结构中不仅保存下一结点的地址,还保存上一节点的地址;方便寻找前一节点和后一节点;

带头:在头结点之前有一个哨兵位结点,哨兵位的数据域不存储有效数据,指针域指向头结点;
不带头:没有哨兵位结点,尾插尾删考虑头结点情况;


循环:头结点与尾结点相连;
非循环:头结点与尾结点不相连;


上述情况相互组合,共有8种情况, 实际中使用的链表数据结构,都是带头双向循环链表,带头双向循环链表虽然结构复杂,但是其结构具有很多优势,实现反而简单;
- typedef int LTDataType;
- typedef struct ListNode
- {
- struct ListNode* prev;//前址域-存放前一个结点的地址
- LTDataType data;//数据域
- struct ListNode* next;//后址域-存放后一个结点的地址
- }ListNode;
逻辑图:
物理图:

由图可知,head->prev=head; head->next=head;

由图可知:
head->next=FirstNode;
head->prev=FirstNode;
FirstNode->prev=head;
FirstNode->next=head;

由图可知:
head->next=firstnode;
head->prev=tail;
tail->next=head;
firstnode->prev=head;

- //创建链表结点,返回链表结点地址
- ListNode* BuyListNode(LTDataType x)
- {
- ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
- if (newnode == NULL)
- {
- perror("malloc failed:");
- exit(-1);
- }
-
- newnode->data = x;
- newnode->next = NULL;
- newnode->prev = NULL;
-
- return newnode;
- }
注:函数调用时得到动态开辟的链表空间起始地址的两种方案如下
方案一: 当传参时为链表结点的地址,函数的形参设计为二级指针,只有通过传址调用,可以将动态开辟的链表的起始地址带出函数;
方案二: 设计函数的返回类型为结点指针,返回动态开辟的链表结点指针,如此可以得到链表空间的起始地址;
- //初始化链表(空链表)
- ListNode* ListInit()
- {
- //创建哨兵位结点
- ListNode* head = BuyListNode(0);//0不是有效数据
-
- //初始化哨兵位结点的指针域
- head->next = head;
- head->prev = head;
-
- return head;
- }
- 循环遍历释放结点,包含哨兵位结点;
- 释放前保存下一结点地址,避免地址丢失;
- //销毁链表,包含哨兵位结点
- void DestoryList(ListNode* phead)
- {
- assert(phead);
-
- //创建寻址指针
- ListNode* cur = phead;
- //断开循环链表
- phead->prev->next = NULL;
- while (cur != NULL)
- {
- //记录下一结点地址
- ListNode* next = cur->next;
- //释放当前结点
- free(cur);
- //寻找下一节点
- cur = next;
- }
- return;
- }
- 循环遍历链表打印数据,不显示哨兵位结点的数据域;
- 以哨兵位头结点作为结束标志;
- void PrintList(ListNode* phead)
- {
- assert(phead != NULL);
-
- ListNode* cur = phead->next;
-
- printf("phead<==>");
- while (cur != phead)
- {
- printf("%d<==>", cur->data);
- cur = cur->next;
- }
- printf("\n");
- }
- 尾插先找尾,哨兵位的前址域即为尾结点即tail=head->prev;
- 当链表为空时,连接的逻辑关系相同(创建三个指针变量,按照新结点的前址域指向谁,谁指向新结点,新结点的后址域指向谁,谁指向新结点进行连接);
- void ListPushBack(ListNode* phead, LTDataType x)
- {
- assert(phead);
-
- //寻找尾结点
- ListNode* tail = phead->prev;
- //创建新结点
- ListNode* newnode = BuyListNode(x);
-
- //尾插
- newnode->prev = tail;
- tail->next = newnode;
-
- newnode->next = phead;
- phead->prev = newnode;
- }
- void Test1()
- {
- ListNode* plist=ListInit();
-
- ListPushBack(plist, 1);
- ListPushBack(plist, 2);
- ListPushBack(plist, 3);
- ListPushBack(plist, 4);
- ListPushBack(plist, 5);
- PrintList(plist);
- }
- int main()
- {
- Test1();
- return 0;
- }
运行结果:

- 头插前先保存哨兵位结点的下一节点即原先真正的首节点;
- 按照按照新结点的前址域指向谁,谁指向新结点,新结点的后址域指向谁,谁指向新结点进行连接从而实现头插,链表为空时,头插逻辑仍然相同;
- //链表头插
- void ListPushFront(ListNode* phead, LTDataType x)
- {
- assert(phead);
-
- //保存原先的首节点
- ListNode* firstnode = phead->next;
- //创建新结点
- ListNode* newnode = BuyListNode(x);
-
- //头插
- newnode->prev = phead;
- phead->next = newnode;
-
- newnode->next = firstnode;
- firstnode->prev = newnode;
- }
- void Test2()
- {
- ListNode* plist = ListInit();
- ListPushFront(plist, 10);
- ListPushFront(plist, 20);
- ListPushFront(plist, 30);
- ListPushFront(plist, 40);
- ListPushFront(plist, 50);
- PrintList(plist);
-
- }
- int main()
- {
- Test2();
- return 0;
- }
运行结果:

- 链表中只剩哨兵位结点,此时链表为空,不再进行尾删;
- 尾删前记录前一节点的地址,方便修改逻辑关系;
- //链表尾删
- void ListPopBack(ListNode* phead)
- {
- assert(phead);
-
- //链表中只剩哨兵位的情况
- assert(phead->next != phead);
-
- //查找尾结点
- ListNode* tail = phead->prev;
- //保存尾结点的上一节点
- ListNode* tailprev = tail->prev;
-
- //尾删
- free(tail);
- //建立链接关系
- tailprev->next = phead;
- phead->prev = tailprev;
-
- }
- void Test3()
- {
- ListNode* plist = ListInit();
-
- ListPushBack(plist, 1);
- ListPushBack(plist, 2);
- ListPushBack(plist, 3);
- ListPushBack(plist, 4);
- ListPushBack(plist, 5);
- PrintList(plist);
-
- ListPopBack(plist);
- PrintList(plist);
-
- ListPopBack(plist);
- PrintList(plist);
-
- ListPopBack(plist);
- PrintList(plist);
-
- }
- int main()
- {
- Test3();
- return 0;
- }
运行结果:

- 链表中只剩哨兵位结点,此时链表为空,不再进行头删;
- 头删前记录下一节点的地址,方便修改逻辑关系;
- //链表头删
- void ListPopFront(ListNode* phead)
- {
- assert(phead);
- //只剩哨兵位,不再头删
- assert(phead->next != phead);
-
- //保存原先的首节点
- ListNode* head = phead->next;
-
- //保存首结点的下一节点
- ListNode* headnext = phead->next->next;
-
- //头删
- free(head);
- //建立链接关系
- headnext->prev = phead;
- phead->next = headnext;
-
- }
- void Test4()
- {
- ListNode* plist = ListInit();
-
- ListPushBack(plist, 1);
- ListPushBack(plist, 2);
- ListPushBack(plist, 3);
- ListPushBack(plist, 4);
- ListPushBack(plist, 5);
- PrintList(plist);
-
- ListPopFront(plist);
- PrintList(plist);
-
- ListPopFront(plist);
- PrintList(plist);
-
- ListPopFront(plist);
- PrintList(plist);
- }
- int main()
- {
- Test4();
- return 0;
- }
运行结果:

- 循环遍历链表,从首节点开始遍历,以哨兵位头结点作为结束标志;
- 根据数据域进行查找,找到返回数据域的结点地址,找不到返回空指针;
- ListNode* ListFind(ListNode* phead, LTDataType x)
- {
- assert(phead);
-
- //创建遍历指针
- ListNode* cur = phead->next;
-
- //遍历链表
- while (cur != phead)
- {
- if ((cur->data) == x)
- {
- //找到返回下标
- return cur;
- }
- cur = cur->next;
- }
- //没找到返回空指针
- return NULL;
- }
- 前插时保存pos位置的前一个节点,方便修改逻辑关系;
- 按照按照新结点的前址域指向谁,谁指向新结点,新结点的后址域指向谁,谁指向新结点进行链接;
- void ListInsert(ListNode* pos, LTDataType x)
- {
- assert(pos != NULL);
-
- //创建新结点
- ListNode* newnode = BuyListNode(x);
- //保存pos位置的前一个结点
- ListNode* posprev = pos->prev;
-
- //前插
- newnode->prev = posprev;
- posprev->next = newnode;
- newnode->next = pos;
- pos->prev = newnode;
- }
- void Test5()
- {
- ListNode* plist = ListInit();
- ListPushBack(plist, 1);
- ListPushBack(plist, 2);
- ListPushBack(plist, 3);
- ListPushBack(plist, 4);
- ListPushBack(plist, 5);
-
- int x = 0;
- printf("请输入查找的数值:");
- scanf("%d", &x);
- ListNode* pos = ListFind(plist, x);
- if (pos == NULL)
- {
- printf("要查找的值不存在\n");
- return;
- }
- //在查找到数值前插入100
- ListInsert(pos, 100);
- PrintList(plist);
-
- }
- int main()
- {
- Test5();
- return 0;
- }
运行结果:

- 链表删除pos位置处的结点前先保存前结点和后结点的地址,方便处理链接关系;
- //双向链表删除pos位置
- void ListEarse(ListNode* pos)
- {
- assert(pos);
-
- //保存pos位置处的前一个和后一个结点;
- ListNode* posprev = pos->prev;
- ListNode* posnext = pos->next;
- //删除pos位置结点
- free(pos);
-
- //建立前后节点的链接关系
- posprev->next = posnext;
- posnext->prev = posprev;
-
- }
- void Test6()
- {
- ListNode* plist = ListInit();
- ListPushBack(plist, 1);
- ListPushBack(plist, 2);
- ListPushBack(plist, 3);
- ListPushBack(plist, 4);
- ListPushBack(plist, 5);
- PrintList(plist);
-
- int x = 0;
- printf("请输入删除的数值:");
- scanf("%d", &x);
- ListNode* pos = ListFind(plist, x);
- if (pos == NULL)
- {
- printf("要删除的值不存在\n");
- return;
- }
-
- ListEarse(pos);
- PrintList(plist);
- }
- int main()
- {
- Test6();
- return 0;
- }
运行结果:

- void Listpushback(ListNode* phead, LTDataType x)
- {
- assert(phead);
- ListInsert(phead, x);
- }
- void Listpushfront(ListNode* phead, LTDataType x)
- {
- assert(phead);
- ListInsert(phead->next, x);
- }
- void Listpopfront(ListNode* phead)
- {
- assert(phead);
- //只剩哨兵位,不再头删
- assert(phead->next != phead);
-
- ListEarse(phead->next);
- }
- void Listpopback(ListNode* phead)
- {
- assert(phead);
-
- //链表中只剩哨兵位的情况
- assert(phead->next != phead);
-
- ListEarse(phead->prev);
- }
- int ListLength(ListNode* phead)
- {
- assert(phead);
-
- int size = 0;
- ListNode* cur = phead->next;
- while (cur != phead)
- {
- ++size;
- cur = cur->next;
- }
- return size;
- }