目录
简介
与单链表相比,带头双向循环链表主要有以下特点:
1.节点中新增一个节点,存储的是前一个结点的地址;
2.链表的头节点存储了尾节点的地址,尾结点存储了头节点的地址;
3.具有单链表所不具备的哨兵位,且哨兵位中不存储有效数据。
常见操作
定义
与无头单向非循环链表相比,带头双向循环链表的定义中多出了新的指针prev,这个指针用于存放前一个节点的地址。如下:
typedef int DataType;
typedef struct SlistNode
{
DataType data;
struct SlistNode*next;
struct SlistNode*prev;
}SlistNode;
初始化
与单向链表不同的是,带头双向循环链表的初始化需要申请一个节点作为哨兵位节点。
ListNode* ListCreate()
{
ListNode* phead = BuyNewNode(-1);
// 这里的-1没有意义,只是为了创建哨兵位
phead->_next = phead;
phead->_prev = phead;
return phead;
}
在这里使用了函数BuyNewNode用于动态开辟节点。
ListNode* BuyNewNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(struct ListNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->_prev = NULL;
newnode->_next = NULL;
newnode->_data = x;
return newnode;
}
需要注意的是:哨兵位节点不存储有效数据;且由于链表循环,初始化时需要将哨兵位的prev和next均指向自身。
可图示如下:

头插
即在链表的头部进行节点的插入。
由于这类链表在初始化时已经创建了哨兵位,即头插是插入在哨兵位的下一个节点的;且哨兵位不可为空,使用assert进行断言。
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
// 开辟包含新增数据x的节点
ListNode* newnode = BuyNewNode(x);
// 链接newnode与pHead->_next
newnode->_next = pHead->_next;
pHead->_next->_prev = newnode;
pHead->_next = newnode;
newnode->_prev = pHead;
}
可图示如下:

本文详细介绍了带头双向循环链表的特点,包括其节点结构、初始化、常见操作如头插、尾插、头删、尾删、查找、插入、删除等,并探讨了其优缺点。链表的初始化涉及哨兵位的使用,头插和尾插操作在有哨兵位的情况下简化了插入过程,而头删和尾删则避免了遍历整个链表。此外,通过代码复用实现了插入和删除的高效性。
最低0.47元/天 解锁文章
2509

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



