链表的概念和结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
结构:实际中链表的结构非常多样,以下情况组合起来就有8种链表结构。
(1)单向、双向
(2)带头、不带头
(3)循环、非循环
本篇主要详解带头双向循环链表,结构如图:
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。
链表的实现
首先在头文件中定义结构体和提供接口:
#pragmaonce#include<stdio.h>#include<stdlib.h>#include<assert.h>typedefint LTDataType;typedefstructListNode{structListNode* next;structListNode* prev;
LTDataType data;}ListNode;voidListPrint(ListNode* phead);//打印链表
ListNode*BuyListNode(LTDataType x);//开辟新节点
ListNode*ListInit();//初始化哨兵位voidListPushBack(ListNode* phead, LTDataType x);//尾插,只要不改变头指针的内容,就不用传二级指针voidListPushFront(ListNode* phead, LTDataType x);//头插voidListPopBack(ListNode* phead);//尾删voidListPopFront(ListNode* phead);//头删
ListNode*ListFind(ListNode* phead, LTDataType x);//查找voidListInsert(ListNode* pos, LTDataType x);//pos位置前插voidListErase(ListNode* pos);//pos位置删除
其次在源文件中实现接口功能:
(1)链表打印
voidListPrint(ListNode* phead){
ListNode* cur = phead;while(cur != phead){printf("%d", cur->data);
cur = cur->next;//找到下一个节点}printf("\n");}
(2)开辟新节点
ListNode*BuyListNode(LTDataType x){
ListNode* node =(ListNode*)malloc(sizeof(ListNode));
node->next =NULL;
node->prev =NULL;
node->data = x;return node;}
(3)初始化哨兵位
ListNode*ListInit(){
ListNode* phead =BuyListNode(0);
phead->next = phead;
phead->prev = phead;return phead;}
(4)尾插
带头双向循环链表的头插尾插非常方便,只需要找到头(phaed->next)和尾(phead->prev)并链接
voidListPushBack(ListNode* phead, LTDataType x){//无论是0个(只有哨兵位)还是多个节点都可以,与节点个数无关assert(phead);
ListNode* tail = phead->prev;//找尾
ListNode* newnode =BuyListNode(x);
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;}
(5)头插
voidListPushFront(ListNode* phead, LTDataType x){assert(phead);
ListNode* first = phead->next;
ListNode* newnode =BuyListNode(x);//phead newnode first
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;}
(6)尾删
voidListPopBack(ListNode* phead){assert(phead);assert(phead->next != phead);
ListNode* tail = phead->prev;
ListNode* tailPrev = tail->prev;free(tail);
tailPrev->next = phead;
phead->prev = tailPrev;}
(7)头删
voidListPopFront(ListNode* phead){assert(phead);//没有节点assert(phead->next != phead);//只有phead
ListNode* first = phead->next;
ListNode* second = first->next;free(first);
phead->next = second;
second->prev = phead;}
(8)查找x
ListNode*ListFind(ListNode* phead, LTDataType x){assert(phead);
ListNode* cur = phead->next;while(cur != phead){if(cur->data == x){return cur;}
cur = cur->next;}returnNULL;}
(9)pos位置前插
voidListInsert(ListNode* pos, LTDataType x){assert(pos);
ListNode* prev = pos->prev;
ListNode* newnode =BuyListNode(x);
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;}
(10)pos位置删除****
voidListErase(ListNode* pos){assert(pos);
ListNode* prev = pos->prev;
ListNode* next = pos->next;
prev->next = next;
next->prev = prev;free(pos);}
主函数的设计大家可以自由发挥,做个简单的测试功能调用函数或是创建菜单栏实现交互都可以。我水平有限,请朋友们谅解!写的不好的地方还请大佬们指出。最后,如果这篇文章对你有帮助,就点个赞或者收藏评论一下吧,谢谢大家支持😁。
下期预告——栈和队列详解!
版权归原作者 阿龙还在写代码 所有, 如有侵权,请联系我们删除。