> 作者简介:დ旧言~,目前大一,现在学习Java,c,c++,Python等
> 座右铭:松树千年终是朽,槿花一日自为荣。
> 望小伙伴们点赞👍收藏✨加关注哟💕💕

前面我们已经学习了顺序表和单链表,顺序表可以存储动态的数据,但是一旦元素过少,而又要开辟空间,这样就造成空间的浪费,而单链表以节点为单位存储,不支持随机访问,只能从头到尾遍历访问,为了解决上面两个问题,人们发现了双链表,把一个一个元素以链子的形式存储,可以存储相互的地址,那双链表如何实现呢,今天咱们就实现一下--《双链表》。
咱们从三个方面实现双链表,动态管理,头插头删尾插尾删,增删查改。
在程序中为了实现双链表,需要创建头文件List.h ,创建源文件Test.c,List.c。
既然实现双链表,初始化动态的双链表必不可少,从两个方面实现初始化动态的双链表。
1.首先我们在List.h定义动态的双链表,省得我们再定义节点(双链表)。
- //定义数据类型
- typedef int LTDataType;
- //定义双链表初始化
- typedef struct ListNode
- {
- //上一个
- struct ListNode* next;
- //下一个
- struct ListNode* prev;
- LTDataType data;
- }LTNode;
2.对双链表进行初始化
我们要明白,这里不像单链表一样,形成节点就行,还需要初始化。
💦这里采用malloc开辟空间
💦采用预指令判断空间是否开辟完成(没有开辟空间返回-1)
💦之后就是简单的初始数据
💦记得返回值
- //初始化
- LTNode* BuyLTNode(LTDataType x)
- {
- //开辟空间
- LTNode* node = (LTNode*)malloc(sizeof(LTNode));
- //判断开辟的空间是否为空
- if (node == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
- //初始化
- node->data = x;
- node->next = NULL;
- node->prev = NULL;
- return node;
- }
-
- //形成双链表
- LTNode* LTInit()
- {
- //使头为0
- LTNode* phead = BuyLTNode(0);
- //构成循环
- phead->next = phead;
- phead->prev = phead;
-
- return phead;
- }
双链表的内存释放与单链表的内存释放有一定的区别,这里我们分开两类,清理与销毁
清理的代码如下:
- //清理
- void ListClear(LTNode* phead)
- {
- //断言
- assert(phead);
-
- //清理全部数据,保留头结点
- LTNode* cur = phead->next;
-
- //循环销毁
- while (cur != phead)
- {
- LTNode* next = cur->next;
- free(cur);
- cur = NULL;
- cur = next;
- }
- }
销毁的代码如下:
- //销毁
- void ListDestory(LTNode** pphead)
- {
- //断言
- assert(*pphead);
-
- //调用清理(函数)
- ListClear(*pphead);
-
- //释放内存
- free(*pphead);
- *pphead = NULL;
- }
打印元素就太简单了,直接上代码![]()
![]()
- //打印数据
- void LTPrint(LTNode* phead)
- {
- //断言
- assert(phead);
-
- printf("phead<=>");
-
- LTNode* cur = phead->next;
- //注意循环的结束的语句
- while (cur != phead)
- {
- printf("%d<=>", cur->data);
- cur = cur->next;
- }
-
- printf("\n");
- }
有了这个图就对指向的改变就轻轻松松![]()
![]()
- //尾插(重点)
- void LTPushBack(LTNode* phead, LTDataType x)
- {
- //断言
- assert(phead);
-
- LTNode* tail = phead->prev;
-
- //给添加的元素创建节点
- LTNode* newnode = BuyLTNode(x);
-
- //新开辟的元素下一个节点指向尾
- newnode->prev = tail;
- //尾的上一个节点指向新的元素
- tail->next = newnode;
-
- //新元素的上一个节点指向头
- newnode->next = phead;
- //头的下一节点指向新的元素节点
- phead->prev = newnode;
-
- //本质上与尾插相似 (双向链表在pos的前面进行插入)
- //LTInsert(phead, x);
- }
双链表重在画图,希望小伙伴们能看得懂。![]()
![]()
- //尾删
- void LTPopBack(LTNode* phead)
- {
- //断言
- assert(phead);
- //防止没有头指向没有元素
- assert(phead->next != phead);
-
- //找到尾
- LTNode* tail = phead->prev;
- //找到尾前面一个元素
- LTNode* tailPrev = tail->prev;
-
- //释放内存
- free(tail);
-
- //(把尾的上一个元素)的上一个指针 指向头
- tailPrev->next = phead;
- //头的下一个指针指向尾的前一个元素
- phead->prev = tailPrev;
-
- // 双向链表删除pos位置的结点(本质上与尾删相似)
- //LTErase(phead->prev);
- }
博主在这里采用三种方法,希望大家至少学会一种![]()
![]()
- //头插
- void LTPushFront(LTNode* phead, LTDataType x)
- {
- assert(phead);
-
- //方法一
- //初始化
- //LTNode* newnode = BuyLTNode(x);
- //newnode->next = phead->next;
- //phead->next->prev = newnode;
- //phead->next = newnode;
- //newnode->prev = phead;
-
- //方法二
- //初始化
- LTNode* newnode = BuyLTNode(x);
- LTNode* first = phead->next;
- phead->next = newnode;
- newnode->prev = phead;
- newnode->next = first;
- first->prev = newnode;
-
- //方法三
- // 双向链表删除pos位置的结点(本质上就是头插)
- //LTInsert(phead->next, x);
- }
💤头删(重点)
双链表会自带梢兵位(这个到后期博主会讲)
- //头删(重点)
- void LTPopFront(LTNode* phead)
- {
- //断言
- assert(phead);
- //头指向不能为空
- assert(phead->next != phead);
-
- //找到梢兵位的节点
- LTNode* first = phead->next;
- //找到头后面一个元素
- LTNode* second = first->next;
-
- //释放内存
- free(first);
-
- //梢兵位的节点指向头后面一个元素
- phead->next = second;
- //后面一个元素指向梢兵位的节点
- second->prev = phead;
-
- //双向链表删除pos位置的结点(本质上和头删一样)
- //LTErase(phead->next);
- }
这个函数还是比较简单的,注意循环的停止条件。
- //统计双链表元素个数
- int LTSize(LTNode* phead)
- {
- //断言
- assert(phead);
-
- int size = 0;
- LTNode* cur = phead->next;
- while (cur != phead)
- {
- ++size;
- cur = cur->next;
- }
-
- return size;
- }
这里大家就看图理解就行啦
- // 双向链表在pos的前面进行插入
- void LTInsert(LTNode* pos, LTDataType x)
- {
- //断言
- assert(pos);
-
- LTNode* posPrev = pos->prev;
- //为插入的元素开辟空间
- LTNode* newnode = BuyLTNode(x);
-
- posPrev->next = newnode;
- newnode->prev = posPrev;
- newnode->next = pos;
- pos->prev = newnode;
- }
这里大家就看图理解就行啦
- // 双向链表删除pos位置的结点
- void LTErase(LTNode* pos)
- {
- //断言
- assert(pos);
- LTNode* posPrev = pos->prev;
- LTNode* posNext = pos->next;
-
- //释放内存
- free(pos);
-
- posPrev->next = posNext;
- posNext->prev = posPrev;
- }
- //包含文件
- #include"List.h"
-
- void TestList1()
- {
- LTNode* plist = LTInit();
- LTPushBack(plist, 1);
- LTPushBack(plist, 2);
- LTPushBack(plist, 3);
- LTPushBack(plist, 4);
- LTPushBack(plist, 5);
- LTPrint(plist);
-
- LTPushFront(plist, 10);
- LTPushBack(plist, 10);
-
- LTPrint(plist);
- }
-
- void TestList2()
- {
- LTNode* plist = LTInit();
- LTPushBack(plist, 1);
- LTPushBack(plist, 2);
- LTPushBack(plist, 3);
- LTPushBack(plist, 4);
- LTPushBack(plist, 5);
- LTPrint(plist);
-
- LTPopBack(plist);
- //LTPopFront(plist);
- LTPrint(plist);
-
- //LTPopFront(plist);
- //LTPopFront(plist);
- //LTPopFront(plist);
- //LTPopFront(plist);
- LTPrint(plist);
- }
-
- //void TestList3()
- //{
- // LTNode* plist = LTInit();
- // LTPushBack(plist, 1);
- // LTPushBack(plist, 2);
- // LTPushBack(plist, 3);
- // LTPushBack(plist, 4);
- // LTPushBack(plist, 5);
- // LTPrint(plist);
- //
- // LTPushFront(plist, 10);
- // LTPushFront(plist, 20);
- // LTPushFront(plist, 30);
- // LTPushFront(plist, 40);
- // LTPrint(plist);
- //}
- //
- //void TestList4()
- //{
- // LTNode* plist = LTInit();
- // LTPushBack(plist, 1);
- // LTPushBack(plist, 2);
- // LTPushBack(plist, 3);
- // LTPushBack(plist, 4);
- // LTPushBack(plist, 5);
- // LTPrint(plist);
- //
- // LTPopFront(plist);
- // LTPrint(plist);
- //
- // LTPopBack(plist);
- // LTPrint(plist);
- //}
-
- int main()
- {
- TestList1();
-
- return 0;
- }
- //包含头文件
- #include
- #include
- #include
-
- //定义数据类型
- typedef int LTDataType;
- //定义双链表初始化
- typedef struct ListNode
- {
- //上一个
- struct ListNode* next;
- //下一个
- struct ListNode* prev;
- LTDataType data;
- }LTNode;
-
- //初始化
- LTNode* BuyLTNode(LTDataType x);
-
- //形成双链表
- LTNode* LTInit();
-
- //打印数据
- void LTPrint(LTNode* phead);
-
- //尾插(重点)
- void LTPushBack(LTNode* phead, LTDataType x);
- //尾删(重点)
- void LTPopBack(LTNode* phead);
- //头插(重点)
- void LTPushFront(LTNode* phead, LTDataType x);
- //头删(重点)
- void LTPopFront(LTNode* phead);
-
- //统计双链表元素个数
- int LTSize(LTNode* phead);
- // 双向链表查找(在双链表中不合适)
- LTNode* LTFind(LTNode* phead, LTDataType x);
-
- // 双向链表在pos的前面进行插入
- void LTInsert(LTNode* pos, LTDataType x);
- // 双向链表删除pos位置的结点
- void LTErase(LTNode* pos);
-
- //清理
- void ListClear(LTNode* phead);
- //销毁
- void ListDestory(LTNode** pphead);
-
-
- //包含文件
- #include"List.h"
-
- //初始化
- LTNode* BuyLTNode(LTDataType x)
- {
- //开辟空间
- LTNode* node = (LTNode*)malloc(sizeof(LTNode));
- //判断开辟的空间是否为空
- if (node == NULL)
- {
- perror("malloc fail");
- exit(-1);
- }
- //初始化
- node->data = x;
- node->next = NULL;
- node->prev = NULL;
- return node;
- }
-
- //形成双链表
- LTNode* LTInit()
- {
- //使头为0
- LTNode* phead = BuyLTNode(0);
- //构成循环
- phead->next = phead;
- phead->prev = phead;
-
- return phead;
- }
-
- //打印数据
- void LTPrint(LTNode* phead)
- {
- //断言
- assert(phead);
-
- printf("phead<=>");
-
- LTNode* cur = phead->next;
- //注意循环的结束的语句
- while (cur != phead)
- {
- printf("%d<=>", cur->data);
- cur = cur->next;
- }
-
- printf("\n");
- }
-
- //尾插(重点)
- void LTPushBack(LTNode* phead, LTDataType x)
- {
- //断言
- assert(phead);
-
- LTNode* tail = phead->prev;
-
- //给添加的元素创建节点
- LTNode* newnode = BuyLTNode(x);
-
- //新开辟的元素下一个节点指向尾
- newnode->prev = tail;
- //尾的上一个节点指向新的元素
- tail->next = newnode;
-
- //新元素的上一个节点指向头
- newnode->next = phead;
- //头的下一节点指向新的元素节点
- phead->prev = newnode;
-
- //本质上与尾插相似 (双向链表在pos的前面进行插入)
- //LTInsert(phead, x);
- }
-
- //尾删
- void LTPopBack(LTNode* phead)
- {
- //断言
- assert(phead);
- //防止没有头指向没有元素
- assert(phead->next != phead);
-
- //找到尾
- LTNode* tail = phead->prev;
- //找到尾前面一个元素
- LTNode* tailPrev = tail->prev;
-
- //释放内存
- free(tail);
-
- //(把尾的上一个元素)的上一个指针 指向头
- tailPrev->next = phead;
- //头的下一个指针指向尾的前一个元素
- phead->prev = tailPrev;
-
- // 双向链表删除pos位置的结点(本质上与尾删相似)
- //LTErase(phead->prev);
- }
-
- //头插
- void LTPushFront(LTNode* phead, LTDataType x)
- {
- assert(phead);
-
- //方法一
- //初始化
- //LTNode* newnode = BuyLTNode(x);
- //newnode->next = phead->next;
- //phead->next->prev = newnode;
- //phead->next = newnode;
- //newnode->prev = phead;
-
- //方法二
- //初始化
- LTNode* newnode = BuyLTNode(x);
- LTNode* first = phead->next;
- phead->next = newnode;
- newnode->prev = phead;
- newnode->next = first;
- first->prev = newnode;
-
- //方法三
- // 双向链表删除pos位置的结点(本质上就是头插)
- //LTInsert(phead->next, x);
- }
-
- //头删(重点)
- void LTPopFront(LTNode* phead)
- {
- //断言
- assert(phead);
- //头指向不能为空
- assert(phead->next != phead);
-
- //找到梢兵位的节点
- LTNode* first = phead->next;
- //找到头后面一个元素
- LTNode* second = first->next;
-
- //释放内存
- free(first);
-
- //梢兵位的节点指向头后面一个元素
- phead->next = second;
- //后面一个元素指向梢兵位的节点
- second->prev = phead;
-
- //双向链表删除pos位置的结点(本质上和头删一样)
- //LTErase(phead->next);
- }
-
- //统计双链表元素个数
- int LTSize(LTNode* phead)
- {
- //断言
- assert(phead);
-
- int size = 0;
- LTNode* cur = phead->next;
- while (cur != phead)
- {
- ++size;
- cur = cur->next;
- }
-
- return size;
- }
-
- // 双向链表在pos的前面进行插入
- void LTInsert(LTNode* pos, LTDataType x)
- {
- //断言
- assert(pos);
-
- LTNode* posPrev = pos->prev;
- //为插入的元素开辟空间
- LTNode* newnode = BuyLTNode(x);
-
- posPrev->next = newnode;
- newnode->prev = posPrev;
- newnode->next = pos;
- pos->prev = newnode;
- }
-
- // 双向链表删除pos位置的结点
- void LTErase(LTNode* pos)
- {
- //断言
- assert(pos);
- LTNode* posPrev = pos->prev;
- LTNode* posNext = pos->next;
-
- //释放内存
- free(pos);
-
- posPrev->next = posNext;
- posNext->prev = posPrev;
- }
-
-
- //清理
- void ListClear(LTNode* phead)
- {
- //断言
- assert(phead);
-
- //清理全部数据,保留头结点
- LTNode* cur = phead->next;
-
- //循环销毁
- while (cur != phead)
- {
- LTNode* next = cur->next;
- free(cur);
- cur = NULL;
- cur = next;
- }
- }
-
- //销毁
- void ListDestory(LTNode** pphead)
- {
- //断言
- assert(*pphead);
-
- //调用清理(函数)
- ListClear(*pphead);
-
- //释放内存
- free(*pphead);
- *pphead = NULL;
- }
今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小说手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。
