hello,我是~小鹿,对你有帮助可以收藏随时找到这里哦
【C语言版数据结构】带头双向循环链表接口函数代码
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表,另外这个结构虽然结构复杂,但是使用代码实现后会发现结构会带来很多又是,实现反而简单了。实现比无头单向非循环更简单。
带头双向循环链表图示:
注意哨兵位头结点(head)不存储有效数据

带头双向循环链表的基本结构:
typedef int LTDataType;//类型重命名,方便修改数据类型
typedef struct ListNode
{
LTDataType data;//结点中存储的数据
struct ListNode* next;//结点中存储的下一个结点的地址
struct ListNode* prev;//结点中存储的前一个结点的地址
}LTNode;
带头双向循环链表的实现:
初始化 ListInit
图示:

代码:
LTNode* ListInit()//初始化
{
LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
phead->next = phead;
phead->prev = phead;
return phead;
}
打印 ListPrint
void ListPrint(LTNode* phead)//打印
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)//因为是循环链表,所以当cur指向phead位置时即打印完毕
{
printf("%d -> ", cur->data);
cur = cur->next;
}
printf("\n");
}
开辟新结点 BuyListNode
LTNode* BuyListNode(LTDataType x)//开辟新结点
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
newnode->data = x;//放入要插入的值
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
尾插 ListPushBack
图示:

代码:
void ListPushBack(LTNode* phead, LTDataType x)//尾插
{
//哨兵位头结点(head)不存储有效数据,头结点是肯定存在的
assert(phead);
LTNode* tail = phead->prev;//tail是最后一个结点的指针
LTNode* newnode = BuyListNode(x);//开辟新结点,之后将这个结点放进链表中
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
//或者使用pos位置之前插入数据函数ListInser也可实现尾插
//ListInsert(phead, x);
}
尾删 ListPopBack
图示:

代码:
void ListPopBack(LTNode* phead)//尾删
{
assert(phead);
assert(phead->next != phead);//哨兵位头结点是不存储有效数据的,尾删时要保证哨兵位存在,若==则已经表示链表为空
LTNode* tail = phead->prev;
LTNode* tailprev = tail->prev;//若不定义倒数第二个指针,尾删再free(tail)后使用的tail为野指针
free(tail);
tail = NULL;
tailprev->next = phead;
phead->prev = tailprev;
//或者使用
//ListErase(phead->prev);
}
头插 ListPushFrond
图示:

代码:
void ListPushFrond(LTNode* phead, LTDataType x)//头插,插在哨兵位后面
{
assert(phead);
LTNode* newnode = BuyListNode(x);//开辟新结点存放x
LTNode* next = phead->next;
newnode->next = next;
next->prev = newnode;
phead->next = newnode;
newnode->prev = phead;
//或者使用pos之前位置插入
//ListInsert(phead->next, x);
}
头删 LTPopFrond
图示:
代码:
void LTPopFrond(LTNode* phead)//头删
{
assert(phead);
assert(phead->next != phead);//链表空
LTNode* next = phead->next;
LTNode* nextnext = next->next;
phead->next = nextnext;
nextnext->prev = phead;
free(next);
next = NULL;
//或者使用
//ListErase(phead->next);
}
查找 ListFind
LTNode* ListFind(LTNode* phead, LTDataType x)//查找
{
assert(phead);
LTNode* cur = phead->next;
while (phead->next != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
在pos的前面进行插入 ListInsert
图示:

代码:
void ListInsert(LTNode* pos, LTDataType x)//在pos的前面进行插入
{
assert(pos);
LTNode* newnode = BuyListNode(x);
LTNode* posprev = pos->prev;
posprev->next = newnode;
newnode->prev = posprev;
newnode->next = pos;
pos->prev = newnode;
}
删除pos位置的结点 ListErase
图示:

代码:
void ListErase(LTNode* pos)//删除pos位置的结点
{
assert(pos);
LTNode* posnext = pos->next;
LTNode* posprev = pos->prev;
posprev->next = posnext;
posnext->prev = posprev;
free(pos);
pos = NULL;
}
销毁 ListDestroy
void ListDestroy(LTNode* phead)//销毁
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;//phead置空不会影响plist。
//但之前的函数都是一级指针,为保持一致性这里也用一级,所以可以调用完这个函数后置空再free(plist);plist=NULL;
}
本文详细介绍了C语言中的带头双向循环链表数据结构,包括初始化、打印、新结点操作(尾插、头插)、删除节点以及链表销毁等关键函数的代码实现和使用方法。

955

被折叠的 条评论
为什么被折叠?



