简介
什么是双向带头循环链表?
上面简单的一个非空
带头循环双向链表逻辑图
如何定义一个双向链表?
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* prev;//前驱指针
LTDataType data;//存放数据
struct ListNode* next;//后驱指针
}ListNode;
根据图和代码可以看双向链表就是单链表的每个结点中,在设置一个指向前驱节点的指针
初始化双向链表
简单认识之后,对他进行初始化(申请一个头节点,让前驱和后驱指针都指向自己)
代码如何写?
首先申请一个新结点(后面插入还需要申请空间 直接封装成函数调用)
ListNode* BuyListNode( LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
接着进行初始化(循环起来)
ListNode* ListNodeInit()
{
ListNode* phead = BuyListNode(0);
phead->next = phead;
phead->prev = phead;
return phead;
}
这里避免使用二级指针,将头结点地址返回即可
尾插
尾插草图
void ListNodePushBack(ListNode* phead, LTDataType x)
{
assert(phead);
//新结点
ListNode* newnode = BuyListNode(x);
//找尾
ListNode* tail = phead->prev;
//连接
newnode->prev = tail;
tail->next = newnode;
newnode->next = phead;
phead->prev = newnode;
}
尾插最重要的就是找尾 而尾 就是phead的前驱
尾删
尾删草图
void ListNodePopBack(ListNode* phead)
{
assert(phead);
//检查空链表 phead->next == phead 就是空链表
assert(phead->next != phead);
//找尾
ListNode* tail = phead->prev;
//尾的前一个
ListNode* tailprev = tail->prev;
//连接
tailprev->next = phead;
phead->prev = tailprev;
//删除
free(tail);
}
尾删时要找尾的前一个 然后释放掉尾即可
头插
头插草图
void ListNodePushFront(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* newnode = BuyListNode(x);
//找之前的头结点
ListNode* pheadnext = phead->next;
//连接新结点
phead->next = newnode;
newnode->prev = phead;
newnode->next = pheadnext;
pheadnext->prev = newnode;
}
头删
头删草图
void ListNodePophFront(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListNode* first = phead->next;
ListNode* second = phead->next->next;
free(first);
phead->next = second;
second->prev = phead;
}
分别记录第一个和第二个结点 释放掉第一个后连接phead和第二个结点即可
在pos之前插入
草图
在pos之前插入时 需要知道pos结点的地址 封装一个FindPos函数进行查找
FindPos实现如下
ListNode* FindPos(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 ListNodeInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* posprev = pos->prev;
ListNode* newnode = BuyListNode(x);
posprev->next = newnode;
newnode->prev = posprev;
newnode->next = pos;
pos->prev = newnode;
}
删除pos位置
通过FindPos函数找到目标位置进行删除
void ListNodePosErase(ListNode* pos)
{
assert(pos);
//保存pos前一个
ListNode* posprev = pos->prev;
//保存pos后一个
ListNode* posnext = pos->next;
free(pos);
posprev->next = posnext;
posnext->prev = posprev;
}
销毁链表
void ListNodeDestroyed(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
ListNode* curnext = NULL;
while (cur != phead)
{
curnext = cur->next;
free(cur);
cur = curnext;
}
}
依次free 先保存下一个 不然free后找不到下一个了
以上基本就是带头双向循环链表的基本结构了