有头双向循环链表
最近接触了单链表,也做了很多题目,但单链表的增删查改真的很麻烦
🌸又最近学习了双向链表,尤其是 有哨兵位头节点的双向循环链表
写起来和用起来真的很爽!!🌸
1. 初始化链表
我们需要一个哨兵位头节点便于尾插头插
🍀代码如下:
//初始化链表
LTNode* ListInit()
{
LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
phead->next = phead;
phead->prev = phead;
return phead;
}
2. 链表尾插
首先创建新的节点
//创建新的节点
LTNode* BuyListNode(LTDateType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
assert(newnode);//没有会警告,按道理malloc都是会成功的
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
🌸插入步骤一:
🌸步骤二:
🌸步骤三:
🌸步骤四:
🍀代码如下:
//尾插
void ListPushBack(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
phead->prev = newnode;
newnode->next = phead;
}
🍀先写一个打印函数用来测试:
//打印链表
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("\n");
}
🍀测试插口:
//测试尾插
void testList1()
{
LTNode* plist = NULL;
plist = ListInit();
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPushBack(plist, 4);
ListPrint(plist);
}
🍀效果:
3. 链表头插
将newnode
插入进去
🌸步骤一和二:
🌸步骤三和四:
🍀代码:
//头插
void ListPushFront(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* next = phead->next;
phead->next = newnode;
newnode->prev = phead;
next->prev = newnode;
newnode->next = next;
}
🍀测试用例:
//测试头插
void testList2()
{
LTNode* plist = NULL;
plist = ListInit();
ListPushFront(plist, 4);
ListPushFront(plist, 3);
ListPushFront(plist, 2);
ListPushFront(plist, 1);
ListPrint(plist);
}
🍀效果:
4. 链表尾删
将最后一个节点删除
新的尾节点是tail
🌸第一步:
🌸第二步:
🌸第三步:
🍀代码:
//尾删
void ListPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);//不能删掉哨兵位
LTNode* tail = phead->prev->prev;
phead->prev = tail;
free(tail->next);
tail->next = phead;
}
🍀测试用例:
//测试尾删
void testList3()
{
LTNode* plist = NULL;
plist = ListInit();
ListPushFront(plist, 4);
ListPushFront(plist, 3);
ListPushFront(plist, 2);
ListPushFront(plist, 1);
//尾删
ListPopBack(plist);
ListPopBack(plist);
ListPopBack(plist);
ListPrint(plist);
}
🍀效果
5. 链表头删
删除链表中cur
🌸第一步:
🌸第二步:
🌸第三步:
🍀代码:
//头删
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* cur = phead->next;
phead->next = cur->next;
cur->next->prev = phead;
free(cur);
}
🍀测试用例:
//测试头删
void testList4()
{
LTNode* plist = NULL;
plist = ListInit();
ListPushFront(plist, 4);
ListPushFront(plist, 3);
ListPushFront(plist, 2);
ListPushFront(plist, 1);
//头删
ListPopFront(plist);
ListPopFront(plist);
ListPopFront(plist);
ListPrint(plist);
}
🍀结果:
6. 链表中间插入
其实这一步和尾插有些类似
🌸步骤图:
7. 链表中间删除
🌸和尾删差不多注意不要删了哨兵
//中间删除
void ListErase(LTNode* pos)
{
assert(pos);
assert(pos->next);//防止哨兵被删
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
}
🍀测试代码
//测试中间插入 + 中间删除
void testList5()
{
LTNode* plist = NULL;
plist = ListInit();
//插入
ListPushFront(plist, 4);
ListPushFront(plist, 3);
ListPushFront(plist, 2);
ListPushFront(plist, 1);
//查找
LTNode* insert = ListFind(plist, 3);
//中间插入
ListInsert(plist->prev, 10);//模拟头插
ListInsert(plist, 11);//模拟尾插
ListInsert(insert, 22);//模拟中间
ListPrint(plist);
//中间删除
ListErase(insert->next);//删除22
ListErase(plist->next);//头删
ListErase(plist->prev);//尾删
ListPrint(plist);
}
🍀测试结果: