目录
1. 无头单向非循环链表 :结构简单,一般不会单独用来存数据。实际中更多是作为 其他数据结构的子结构 ,如 哈希桶、图的邻接表 等等。另外这种结构在 笔试面试 中出现很多。2. 带头双向循环链表 :结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多 优势 ,实现反而 简单了 ,后面我们代码实现了就知道了。
我们来对比单向链表的结构体,看一下与双向链表的结构体有什么区别:(左单向,右双向)
- 我们可以看出双向链表比单向链表多了一个结构体指针prev,它的作用是存储前一个结点的地址。
- 双向链表的每个节点既可以找到前一个节点,也可以找到后一个节点,
- 此外头节点和尾节点比较特殊,头节点的prev指向尾节点,尾节点的next指向头节点。
- 如果链表只有头节点(也叫哨兵位),那么它的prev和next都指向自己。
- typedef int LTDataType;
- typedef struct ListNode
- {
- struct ListNode* next;
- struct ListNode* prev;
- LTDataType data;
- }LTNode;
- LTNode* LTInit()
- {
- LTNode* phead = BuyLTNode(-1);
- phead->prev = phead;
- phead->next = phead;
- return phead;
- }
创建结构体指针,将初始化函数的返回值赋值给结构体指针,即为初始化完成,代码如下:
LTNode* plist = LTInit();
这里也可以用二级指针接收链表地址初始化链表。
- void LTInit(LTNode** phead)
- {
- assert(phead);
- * phead = BuyLTNode(-1);
- (*phead)->prev = *phead;
- (*phead)->next = *phead;
- (*phead)->data = 0;
- }
使用该函数初始化时,使用方法也有变化:
- LTNode* plist;
- LTInit(&plist);
- LTNode* BuyLTNode(LTDataType x)
- {
- LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
- if (newnode == NULL) {
- perror("malloc fail");
- return NULL;
- }
- newnode->data = x;
- newnode->prev = NULL;
- newnode->next = NULL;
- return newnode;
- }
- void LTPrint(LTNode* phead)
- {
- assert(phead);
- printf("guard<==>");
- LTNode* cur = phead->next;
- while (cur != phead) {
- if(cur!=phead->prev)
- printf("%d<==>", cur->data);
- else
- printf("%d\n", cur->data);
- cur = cur->next;
- }
- }
《头插&尾插 和 头删&尾删》我们都可以通过后续的指定位置前插入函数和删除函数实现, 这里进行讲解为了更好得熟悉理解双向链表。
- void LTPushFront(LTNode* phead, LTDataType x)
- {
- assert(phead);
- LTNode* newnode = BuyLTNode(x);
- LTNode* first = phead->next;
- //下面两块代码的顺序可以颠倒,因为使用指针first储存了原来链表第一个元素的地址
- newnode->next = first;
- first->prev = newnode;
-
- phead->next = newnode;
- newnode->prev = phead;
- }
当然下面这种形式也对,只是后两个代码块不能调换顺序,具体原因大家可以画图试一下。
- void LTPushFront(LTNode* phead, LTDataType x)
- {
- assert(phead);
- LTNode* newnode = BuyLTNode(x);
-
- newnode->next = phead->next;
- phead->next->prev = newnode;
-
- newnode->prev = phead;
- phead->next = newnode;
- }
- void LTPushBack(LTNode* phead, LTDataType x)
- {
- assert(phead);
- LTNode* newnode = BuyLTNode(x);
- LTNode* tail = phead->prev;
-
- newnode->next = phead;
- phead->prev = newnode;
-
- tail->next = newnode;
- newnode->prev = tail;
- }
- bool LTEmpty(LTNode* phead)
- {
- assert(phead);
- return phead->next == phead;
- }
-
- void LTPopFront(LTNode* phead)
- {
- assert(phead);
- assert(!LTEmpty(phead));
-
- LTNode* first = phead->next;
- LTNode* second = first->next;
-
- second->prev = phead;
- phead->next = second;
- free(first);
- }
- void LTPopBack(LTNode* phead)
- {
- assert(phead);
- assert(!LTEmpty(phead));
-
- LTNode* tail = phead->prev;
- LTNode* tailPrev = tail->prev;
-
- free(tail);
- tailPrev->next = phead;
- phead->prev = tailPrev;
-
- }
- LTNode* LTFind(LTNode* phead, LTDataType x)
- {
- assert(phead);
- LTNode* cur = phead->next;
- while (cur != phead) {
- if (cur->data == x)
- return cur;
-
- cur = cur->next;
- }
- return NULL;
- }
- void LTInsert(LTNode* pos, LTDataType x)
- {
- assert(pos);
-
- LTNode* prev = pos->prev;
- LTNode* newnode = BuyLTNode(x);
-
- prev->next = newnode;
- newnode->prev = prev;
-
- newnode->next = pos;
- pos->prev = newnode;
- }
- void LTErase(LTNode* pos)//可以判断phead是否等于哨兵位
- {
- assert(pos);
- LTNode* posPrev = pos->prev;
- LTNode* posNext = pos->next;
-
- posPrev->next = posNext;
- posNext->prev = posPrev;
- free(pos);
- }
- void LTDestroy(LTNode** phead)
- {
- assert(phead);
- assert(*phead);
- LTNode* cur = (*phead)->next;
- while (cur != *phead) {
- LTNode* next = cur->next;
- free(cur);
- cur = next;
- }
- free(*phead);
- *phead = NULL;
- }
*phead
是否为非空,为空则报错。在循环结束后,链表中的所有节点都已经被销毁,最后一步是释放链表头指针 *phead
所占用的内存。
*phead = NULL
最后,将链表头指针设置为 NULL
,以确保不再引用链表,避免悬挂指针或野指针的问题。
完整版代码我保留了注释,方便读者学习,以下代码的功能经测试没有问题,放心使用。
- #include
- #include
- #include
- #include
-
- typedef int LTDataType;
- typedef struct ListNode
- {
- struct ListNode* next;
- struct ListNode* prev;
- LTDataType data;
- }LTNode;
-
- //初始化链表
- LTNode* LTInit();
- //void LTInit(LTNode** phead);
-
- //打印链表
- void LTPrint(LTNode* phead);
-
- //头插&尾插
- void LTPushFront(LTNode* phead, LTDataType x);
- void LTPushBack(LTNode* phead, LTDataType x);
-
- //头删&尾删
- void LTPopFront(LTNode* phead);
- void LTPopBack(LTNode* phead);
-
- //查找值为x的节点,返回节点地址
- LTNode* LTFind(LTNode* phead, LTDataType x);
-
- //在指定节点前插入
- void LTInsert(LTNode* pos, LTDataType x);
-
- //删除指定节点
- void LTErase(LTNode* pos);
-
- //销毁链表
- void LTDestroy(LTNode** phead);
- #define _CRT_SECURE_NO_WARNINGS 1
- #include "List.h"
-
- //创建新节点
- LTNode* BuyLTNode(LTDataType x)
- {
- LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
- if (newnode == NULL) {
- perror("malloc fail");
- return NULL;
- }
- newnode->data = x;
- newnode->prev = NULL;
- newnode->next = NULL;
- return newnode;
- }
-
- //初始化链表
- LTNode* LTInit()
- {
- LTNode* phead = BuyLTNode(-1);
- phead->prev = phead;
- phead->next = phead;
- return phead;
- }
- //void LTInit(LTNode** phead)
- //{
- // assert(phead);
- // * phead = BuyLTNode(-1);
- // (*phead)->prev = *phead;
- // (*phead)->next = *phead;
- // (*phead)->data = 0;
- //}
-
- //打印链表
- void LTPrint(LTNode* phead)
- {
- assert(phead);
- printf("guard<==>");
- LTNode* cur = phead->next;
- while (cur != phead) {
- if(cur!=phead->prev)
- printf("%d<==>", cur->data);
- else
- printf("%d\n", cur->data);
- cur = cur->next;
- }
- }
-
- //头插
- void LTPushFront(LTNode* phead, LTDataType x)
- {
- LTInsert(phead->next, x);
- }
-
- //void LTPushFront(LTNode* phead, LTDataType x)
- //{
- // assert(phead);
- // LTNode* newnode = BuyLTNode(x);
- // LTNode* first = phead->next;
- // //下面两块代码的顺序可以颠倒,因为使用指针first储存了原来链表第一个元素的地址
- // newnode->next = first;
- // first->prev = newnode;
- //
- // phead->next = newnode;
- // newnode->prev = phead;
- //}
-
- //void LTPushFront(LTNode* phead, LTDataType x)
- //{
- // assert(phead);
- // LTNode* newnode = BuyLTNode(x);
- //
- // newnode->next = phead->next;
- // phead->next->prev = newnode;
- //
- // newnode->prev = phead;
- // phead->next = newnode;
- //}
-
- //尾插
- void LTPushBack(LTNode* phead, LTDataType x)
- {
- assert(phead);
-
- LTInsert(phead, x);
- }
- //void LTPushBack(LTNode* phead, LTDataType x)
- //{
- // assert(phead);
- // LTNode* newnode = BuyLTNode(x);
- // LTNode* tail = phead->prev;
- //
- // newnode->next = phead;
- // phead->prev = newnode;
- //
- // tail->next = newnode;
- // newnode->prev = tail;
- //}
-
- //监测链表是否为空
- bool LTEmpty(LTNode* phead)
- {
- assert(phead);
- return phead->next == phead;
- }
-
- //头删
- void LTPopFront(LTNode* phead)
- {
- assert(phead);
- assert(!LTEmpty(phead));
-
- LTErase(phead->next);
- }
-
- //void LTPopFront(LTNode* phead)
- //{
- // assert(phead);
- // assert(!LTEmpty(phead));
- //
- // LTNode* first = phead->next;
- // LTNode* second = first->next;
- //
- // second->prev = phead;
- // phead->next = second;
- // free(first);
- //}
-
-
- //尾删
- void LTPopBack(LTNode* phead)
- {
- assert(phead);
- assert(!LTEmpty(phead));
-
- LTErase(phead->prev);
- }
-
- //void LTPopBack(LTNode* phead)
- //{
- // assert(phead);
- // assert(!LTEmpty(phead));
- // //LTErase(phead->prev);
- //
- // LTNode* tail = phead->prev;
- // LTNode* tailPrev = tail->prev;
- //
- // free(tail);
- // tailPrev->next = phead;
- // phead->prev = tailPrev;
- //
- //}
-
-
-
-
- //查找值为x的节点,返回节点地址
- LTNode* LTFind(LTNode* phead, LTDataType x)
- {
- assert(phead);
- LTNode* cur = phead->next;
- while (cur != phead) {
- if (cur->data == x)
- return cur;
-
- cur = cur->next;
- }
- return NULL;
- }
-
- //在指定节点前插入,与LTFind搭配使用
- void LTInsert(LTNode* pos, LTDataType x)
- {
- assert(pos);
-
- LTNode* prev = pos->prev;
- LTNode* newnode = BuyLTNode(x);
-
- prev->next = newnode;
- newnode->prev = prev;
-
- newnode->next = pos;
- pos->prev = newnode;
- }
-
- // 删除指定节点
- void LTErase(LTNode* pos)//可以判断phead是否等于哨兵位
- {
- assert(pos);
- LTNode* posPrev = pos->prev;
- LTNode* posNext = pos->next;
-
- posPrev->next = posNext;
- posNext->prev = posPrev;
- free(pos);
- }
-
- //销毁链表
- void LTDestroy(LTNode** phead)
- {
- assert(phead);
- assert(*phead);
- LTNode* cur = (*phead)->next;
- while (cur != *phead) {
- LTNode* next = cur->next;
- free(cur);
- cur = next;
- }
- free(*phead);
- *phead = NULL;
- }
- #include "List.h"
-
- void test1() {
- /*LTNode* plist;
- LTInit(&plist);*/
-
- LTNode* plist = LTInit();
- LTPushFront(plist, 1);
- LTPushFront(plist, 2);
- LTPushFront(plist, 3);
- LTPushFront(plist, 4);
- LTPushBack(plist, 888);
- LTPushBack(plist, 888);
- LTPopFront(plist);
- LTPopBack(plist);
- LTNode* pos = LTFind(plist, 888);
- LTInsert(pos, 999);
-
- LTErase(pos);
-
- LTPrint(plist);
- LTDestroy(&plist);
- }
-
- int main()
- {
- test1();
- return 0;
- }