赞
踩
链表根据是否带头、是否双向、是否循环可以分为八种,双向链表是典型的带头双向循环链表。
双向链表的结构可以表示为下:
- struct ListNode
- {
- int data;
- struct ListNode* next;
- struct ListNode* prev;
- }
双向链表的实现依旧包含List.h,List.c,test.c
双向链表为空的情况:只有一个哨兵位。
先定义一个结构如下:
- typedef int LTDatatype;
- typedef struct ListNode {
- LTDatatype data;
- struct ListNode* next;
- struct ListNode* prev;
- }LTNode;
链表初始化时我们应该先创建一个哨兵位,则实现代码如下:
- LTNode* LTBuyNode(LTDatatype x)
- {
- LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
- if (newnode == NULL)
- {
- perror("malloc fail!");
- exit(1);
- }
- newnode->data = x;
- newnode->next = newnode;
- newnode->prev = newnode;
-
- return newnode;
- }
- //初始化
- void LTInit(LTNode** pphead)
- {
- //创建一个哨兵位
- *pphead = LTBuyNode(-1);
- }

因为在双向链表中有哨兵位,所以在这种情况下不用二级指针,用一级指针就可以实现。
用一级还是二级,要看pphead指向的结点会不会改变,如果发生改变,传二级,如果不发生改变,要传一级。
尾插时发生改变的是头结点和最后一个结点。
第一个结点:第一个有效的结点。
哨兵位:头结点。
代码实现如下:
- //插入数据
- //尾插
- void LTPushBack(LTNode* phead, LTDatatype x)
- {
- assert(phead);
- LTNode* newnode = LTBuyNode(x);
- //从后往前
- newnode->next = phead;
- newnode->prev = phead->prev;
-
- phead->prev->next = newnode;
- phead->prev = newnode;
- }
打印链表的代码:
- //打印链表
- void LTPrint(LTNode* phead)
- {
- LTNode* pcur = phead->next;
- while (pcur != phead)
- {
- printf("%d->", pcur->data);
- pcur = pcur->next;
- }
- printf("\n");
- }
头插的代码实现:
- //头插
- void LTPushFront(LTNode* phead, LTDatatype x)
- {
- assert(phead);
- //相当于插入哨兵位之后
- LTNode* newnode = LTBuyNode(x);
-
- newnode->next = phead->next;
- newnode->prev = phead;
-
- phead->next->prev = newnode;
- phead->next = newnode;
- }
- //判断链表是否为空
- bool LTEmpty(LTNode* phead)
- {
- assert(phead);
- return phead->next == phead;
- }
- //删除数据
- //尾删
- void LTPopBack(LTNode* phead)
- {
- assert(phead);
- assert(!LTEmpty(phead));
-
- LTNode* del = phead->prev;
- LTNode* prev = del->prev;
-
- prev->next = phead;
- phead->prev = prev;
-
- free(del);
- del = NULL;
- }

- //头删
- void LTPopFront(LTNode* phead)
- {
- assert(phead);
- assert(!LTEmpty(phead));
-
- LTNode* del = phead->next;
- del->next->prev = phead;
- phead->next = del->next;
-
- free(del);
- del = NULL;
- }
- //查找
- LTNode* LTFind(LTNode* phead, LTDatatype x)
- {
- assert(phead);
- LTNode* pcur = phead->next;
- while (pcur != phead)
- {
- if (pcur->data == x)
- {
- return pcur;
- }
- pcur = pcur->next;
- }
- return NULL;
- }
- //在指定位置之后插入数据
- void LTInsert(LTNode* pos, LTDatatype x)
- {
- assert(pos);
- LTNode* newnode = LTBuyNode(x);
-
- newnode->next = pos->next;
- newnode->prev = pos;
-
- pos->next->prev = newnode;
- pos->next = newnode;
- }
- //删除指定位置结点
- void LTErase(LTNode* pos)
- {
- assert(pos);
- pos->prev->next = pos->next;
- pos->next->prev = pos->prev;
-
- free(pos);
- pos = NULL;
- }
- //销毁链表
- void LTDestroy(LTNode** pphead)
- {
- assert(pphead && *pphead);
- LTNode* pcur = (*pphead)->next;
- while (pcur != pphead)
- {
- LTNode* Next = pcur->next;
- free(pcur);
- pcur = Next;
- }
- pcur = NULL;
- free(*pphead);
- *pphead = NULL;
- }
在我们写代码时我们总是要考虑传二级指针,还是一级指针,那么有没有什么方法可以进行改进呢?
- void LTDestroy1(LTNode* phead)
- {
- assert(phead);
- LTNode* pcur = phead->next;
- while (pcur != phead)
- {
- LTNode* Next = pcur->next;
- free(pcur);
- pcur = Next;
- }
- free(phead);
- phead = NULL;
- pcur = NULL;
- }
传一级时,需要手动将plist置为NULL。
- LTNode* LTInit1()
- {
- LTNode* phead = LTBuyNode(-1);
- return phead;
- }
在调用时应该用:
- //调用时为
- LTNode* plist = LTInit1();
(该图片来自比特就业课的课件)
在图中我们可以分别出顺序表和链表之间的关系,我们要重点关注其应用场景。
今天就到这里,我们下一个知识点见(* ̄︶ ̄)!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。