链表的逻辑图如下
实现:
尾插
尾删
头插
头删
中间插入
中间删除
1.构造哨兵位
创建一个哨兵位头节点,这个结构体不用存储数据
里面的BuyListNode是用来开辟节点的,因为后面尾插和头插都要用,所以定义成函数
ListNode* BuyListNode(LTDataType x)//申请链表节点
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->next = NULL;
newnode->prev = NULL;
newnode->data = x;
return newnode;
}
ListNode* InitNode()//这个是初始化链表,也可以是定义哨兵位
{
ListNode* pHead = BuyListNode(1);
pHead->next = pHead;
pHead->prev = pHead;
return pHead;
}
2.尾插
首先我们要知道,双向循环链表的优势之一就是不用遍历,这里定义tail保存链表的尾节点,直接访问然后尾插
void ListPushBack(ListNode* pHead, LTDataType x)//尾插
{
assert(pHead);
ListNode* newnode = BuyListNode(x);
ListNode* tail = pHead->prev;//tail = 尾节点
tail->next = newnode;
newnode->prev = tail;
newnode->next = pHead;
pHead->prev = newnode;
}
尾删:
由于我们双链表的头节点的prev就是尾节点,尾节点的prev就是新的尾节点,所以可以很方便的保存新的尾节点,然后断开原来尾节点的指针,然后head的prev指向新的尾节点,新的尾节点的next指向头节点
这里需要判断一下:这里如果只剩一个节点(不包括哨兵位),或者只剩哨兵位。
这里判断情况一样head的prev和next指向同一个节点,如下图,返回哨兵位即可
void ListPopBack(ListNode* pHead)//尾删
{
if (pHead->next == pHead->prev)
{
pHead->prev = pHead;
pHead->next = pHead;
}
ListNode* tail = pHead->prev;
ListNode* lasttail = tail->prev;
lasttail->next = pHead;
pHead->prev = lasttail;
tail->next = NULL;
tail->prev = NULL;
free(tail);
}
头插:
这里我的头插没有判断链表只剩哨兵位的情况,如果是就直接尾插
void ListPushFront(ListNode* pHead, LTDataType x)//头插
{
assert(pHead);
ListNode* cur = pHead->next;
ListNode* node = BuyListNode(x);
pHead->next = node;
node->prev = pHead;
node->next = cur;
cur->prev = node;
}
头删:
这里的判断和尾删一样
void ListPopFront(ListNode* pHead)//头删
{
if (pHead->next == pHead->prev)
{
pHead->prev = pHead;
pHead->next = pHead;
}
ListNode* cur = pHead->next->next;
ListNode* popcur = pHead->next;
popcur->next = NULL;
popcur->prev = NULL;
cur->prev = pHead;
pHead->next = cur;
}
从中间插入:
这里我是想找到想删除的节点
定义一个find函数:
ListNode* ListFind(ListNode* pHead, LTDataType x)//查找节点
{
ListNode* cur = pHead->next;
while (cur != pHead)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
这个删除就和头删步骤大致一样,改变pos前后的prev和next指针的指向
从中间删除
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* prev = pos->prev;
ListNode* next = pos->next;
prev->next = next;
next->prev = prev;
pos->prev = NULL;
pos->next = NULL;
}
这里的打印函数直接从head->next开始,就不用管哨兵位了