前言:
双向带头循环链表应该是链表中非常方便的一种,可以很容易的在任意位置上进行插入和删除,可以很容易的对链表进行管理,如果对单链表的使用不太熟悉的话可以看看这篇文章
https://blog.csdn.net/pythoncjave/article/details/125052430?spm=1001.2014.3001.5502
链表的建立
typedef int LTDateType;
typedef struct ListNode
{
struct ListNode* next;//下一个指针
struct ListNode* prev;//上一个指针
LTDateType data; //数据域
}LTNode;
我们要实现的接口有:
LTNode* BuyListNode(LTDateType x);//建立一个新的节点
LTNode* ListInit(); //链表的初始化
void ListPushBack(LTNode* phead, LTDateType x);//链表的尾插
void ListPrint(LTNode* phead);//打印链表
void ListPustFront(LTNode* phead, LTDateType x);//链表的头插
void ListPopBack(LTNode* phead);//链表的尾删
void ListPopFront(LTNode* phead);//链表的头删
建立一个结点
LTNode* BuyListNode(LTDateType 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* ListInit()
{
LTNode* phead;
phead = BuyListNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
这里将链表的头节点的下一个结点与上一个结点都指向自己,方便后面进行循环判定
链表的尾插
void ListPushBack(LTNode* phead, LTDateType x)
{
LTNode* newnode = BuyListNode(x);
LTNode* tail = phead->prev;
//----------------------------------------
tail->next = newnode;
newnode->next = phead;
newnode->prev = tail;
phead->prev = newnode;
}
尾插一共需要改变四个指针的指向:
①头结点的上一个结点指向(新结点newnode) 指针tail 暂时保存的是插入前的尾结点
②新结点newnode 的 newnode->next指针 应该指向头
③新节点newnode 的newnode->prev指针 应该指向插入前的尾结点
④头节点的上一个结点phead->prev指向新的节点newnode
链表的打印
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
设置一个cur指针去向后遍历,当指向头指针的时候就结束循环
链表的头插
void ListPustFront(LTNode* phead, LTDateType x)
{
if (phead->next == phead)
{
ListPushBack(phead,x);
return;
}
LTNode* newnode = BuyListNode(x);
LTNode* next = phead->next;//第一个结点
//--------------------------------phead指向的事哨兵位
phead->next = newnode;
newnode->next = next;
newnode->prev = phead;
next->prev = newnode;
}
头插也需要改变四个指针的指向:
①将新头给newnode
②newnode->的下一个指向 next
③newnode->上一个指向 phead
④next->prev指向新节点newnode
链表的头删
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != NULL);
LTNode* next = phead->next;
LTNode* next2 = next->next;
phead->next = next2;
next2->prev = phead;
free(next);
}
头删需要改变两个个指针的指向:
①将phead->next指向next2 也就是phead->next->next
②将next->prev指向phead
链表的尾删
void ListPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* tail = phead->prev;
phead->prev = tail->prev;
tail->prev->next = phead;
free(tail);
}
同上所述前面的都能理解的话这个也很容易理解
---------------------------------------------------------------------------------------------------------------------------------
总结:双向带头循环链表的插入 需要改变四个指针的指向 删除需要改变两个指针的指向 并释放空间