数据结构初阶图解链表双向带头循环链表
Posted Bitdancing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构初阶图解链表双向带头循环链表相关的知识,希望对你有一定的参考价值。
单链表在之前的博客里已经详细讲解过了 (指路导航 无哨兵位单向非循环链表),接下来讲解的是 双向带头循环链表。
注意:要转图请提前打招呼
一级指针还是二级指针?
首先我们要确定的问题是带哨兵位的链表,需要传啥类型的实参?
由图见,因为带哨兵位,不对phead进行修改,所以只需要传一级指针。
结点结构体
// 带头+双向+循环链表增删查改
typedef int LTDataType;
typedef struct ListNode {
LTDataType data;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
函数接口
// 创建返回链表的头结点
LTNode* ListCreate();
// 双向链表销毁
void ListDestroy(LTNode* phead);
// 双向链表打印
void ListPrint(LTNode* phead);
// 双向链表尾插
void ListPushBack(LTNode* phead, LTDataType x);
// 双向链表尾删
void ListPopBack(LTNode* phead);
// 双向链表头插
void ListPushFront(LTNode* phead, LTDataType x);
// 双向链表头删
void ListPopFront(LTNode* phead);
// 双向链表查找
LTNode* ListFind(LTNode* phead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(LTNode* pos, LTDataType x);
// 双向链表删除pos位置的结点
void ListErase(LTNode* pos);
创建返回链表的头结点
开辟一个哨兵位结点,结点next和prev指向自己。
LTNode* ListCreate()
{
LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
phead->next = phead;
phead->prev = phead;
return phead;
}
双向链表销毁
- 链表要求逐个销毁
- 因为这里传的是一级指针,free了哨兵位后会使他成为野指针,需要在调用函数处置NULL。
void ListDestroy(LTNode* phead)
{
assert(phead);
// 逐个销毁
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
双向链表打印
逐个打印、注意结束条件即可。
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\\n");
}
双向链表尾插
老套路、不废话。
void ListPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
双向链表尾删
void ListPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead); // 不能删除哨兵位
LTNode* oldtail = phead->prev;
LTNode* newtail = oldtail->prev;
phead->prev = newtail;
newtail->next = phead;
free(oldtail);
}
双向链表头插
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* first = phead->next;
LTNode* newnode = BuyListNode(x);
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;
}
双向链表头删
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* first = phead->next, * second = first->next;
//phead first second
phead->next = second;
second->prev = phead;
free(first);
}
双向链表查找
遍历思想。
LTNode* ListFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
双向链表在pos的前面进行插入
void ListInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* prevnode = pos->prev;
LTNode* newnode = BuyListNode(x);
// prevnode newnode pos
prevnode->next = newnode;
newnode->prev = prevnode;
newnode->next = pos;
pos->prev = newnode;
}
双向链表删除pos位置的结点
void ListErase(LTNode* pos)
{
assert(pos);
LTNode* next = pos->next, * prev = pos->prev;
// prev pos next
prev->next = next;
next->prev = pos;
}
总结
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。
码文不易,欢迎三连,笔芯感谢!
以上是关于数据结构初阶图解链表双向带头循环链表的主要内容,如果未能解决你的问题,请参考以下文章