目录
简介
与单链表相比,带头双向循环链表主要有以下特点:
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;
}
可图示如下: