双向带头循环链表的实现
这个数据结构可以算是YYDS的存在了。
我们前面讲过的单链表,尾删和尾插需要遍历数组,极其不方便。
但今天讲的结构可以算是把单链表的尾部算法完美解决了。
双向带头循环链表
结构讲解
逻辑结构:
这个是双链表的逻辑图。
可以在图中看到,双链表这里面相较于以前的单链表结构,有了哨兵位,前后衔接。
那怎么样可以使前后相接呢?
这时我们先来看看单链表的物理结构
单链表的物理结构是指向后一个节点的指针和一个数据结合
那我们要实现头尾衔接。只要再添加一个指向前一个节点的指针不就好了?
这里我们添加了一个prev的指针,用来指向前一个节点的指针。
接下来就是实现部分了,说是实现部分,其实也和单链表差不多,并且更简单了。
期望实现功能
// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType _data;struct ListNode* _next;struct ListNode* _prev;
}ListNode;// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
创建链表和头节点作用
ListNode* ListCreate()
{ListNode* ptr = (ListNode*)malloc(sizeof(ListNode));assert(ptr);ptr->_next = ptr;ptr->_prev = ptr;ptr->_data = 0;return ptr;
}
这倒是没有什么难度,这个和单链表创建没有什么不同。
只不过第一个返回的这个头节点是哨兵位
在后面进行插入时不能将第一个头节点进行赋值需要新建一个节点出来,删除时不能将哨兵位删除。
至于哨兵位有啥作用,等会就能知道了。
头插和头删
头插
先来一个链表的最强项——头插和头删,单链表是强处,那身为YYDS的双向带头循环链表那肯定也是不弱的
void ListPushFront(ListNode* pHead, LTDataType x)
{
//创建新节点assert(pHead);ListNode* ptr = (ListNode*)malloc(sizeof(ListNode));assert(ptr);
//进行头插ptr->_data = x;ptr->_prev = pHead;ptr->_next = pHead->_next;pHead->_next->_prev = ptr;pHead->_next = ptr;
}
当想要再两者中头插入一个数据时,首先就需要对PNewNode进行插入
//对newNode节点进行赋值ptr->_data = x;ptr->_prev = pHead;ptr->_next = pHead->_next;
前者和后者的红框处需要标点。
其实这个时候就已经体验处哨兵位的好处了,如果没有哨兵位,进行头插和头删时,需要频繁变动头节点,而有了哨兵位,只需要插入就好,操作简洁了很多
pHead->_next->_prev = ptr;pHead->_next = ptr;
最后两个代码就是对红框处进行赋值。
头删
由于头删和头插需要改变的地方一致,所以这里就直接上代码了
void ListPopFront(ListNode* pHead)
{assert(pHead);if (pHead->_next == pHead){printf("只有头节点,无法删除");return;}ListNode* cur = pHead->_next;pHead->_next = cur->_next;cur->_next->_prev = pHead;free(cur);
}
尾插与尾删
尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{ListNode* ptr = (ListNode*)malloc(sizeof(ListNode));assert(ptr);ptr->_data = x;ptr->_next = pHead;ptr->_prev = pHead->_prev;pHead->_prev->_next = ptr;pHead->_prev = ptr;}
现在要对尾部进行插入。
首先要对PNewNode进行设置
ptr->_data = x;ptr->_next = pHead;ptr->_prev = pHead->_prev;
PNewNode设置完以后,接下来就是进行经典的红框修改了。
pHead->_prev->_next = ptr;pHead->_prev = ptr;
尾删
尾删和尾插也大体相同,所以也摆了。
void ListPopBack(ListNode* pHead)
{assert(pHead);if (pHead->_next == pHead){printf("只有头节点,无法删除");return;}pHead->_prev->_prev->_next = pHead;ListNode* cur = pHead->_prev;pHead->_prev = pHead->_prev->_prev;free(cur);
}
pos 删除和插入
插入
void ListInsert(ListNode* pos, LTDataType x)
{
//建立新节点ListNode* ptr = (ListNode*)malloc(sizeof(ListNode));assert(ptr);//插入部分ptr->_data = x;pos->_prev->_next = ptr;ptr->_prev = pos->_prev;ptr->_next = pos;pos->_prev = ptr;
}
这其实和前面的也大同小异
只不过改成了pos而已,要改的地方其实大同小异
同样是对pos和红框部分进行修改即可
ptr->_data = x;pos->_prev->_next = ptr;ptr->_prev = pos->_prev;ptr->_next = pos;pos->_prev = ptr;
删除
void ListErase(ListNode* pos)
{pos->_prev->_next = pos->_next;pos->_next->_prev = pos->_prev;free(pos);
}
打印和查找
// 双向链表打印
void ListPrint(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->_next;printf("head->");while (cur != pHead){printf("%d->", cur->_data);cur = cur->_next;}
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{ListNode* cur = pHead->_next;while (cur != pHead){if (cur->_data == x){return cur;}cur = cur->_next;}
}
这两个都没有什么难度
要注意的点就是,这个双链表是带循环的
所以结束标志是cur重新变成head
整体代码
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType _data;struct ListNode* _next;struct ListNode* _prev;
}ListNode;// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);// 创建返回链表的头结点.
ListNode* ListCreate()
{ListNode* ptr = (ListNode*)malloc(sizeof(ListNode));assert(ptr);ptr->_next = ptr;ptr->_prev = ptr;ptr->_data = 0;return ptr;
}
// 双向链表打印
void ListPrint(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->_next;printf("head->");while (cur != pHead){printf("%d->", cur->_data);cur = cur->_next;}
}
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{ListNode* ptr = (ListNode*)malloc(sizeof(ListNode));assert(ptr);ptr->_data = x;ptr->_next = pHead;ptr->_prev = pHead->_prev;pHead->_prev->_next = ptr;pHead->_prev = ptr;}
// 双向链表尾删
void ListPopBack(ListNode* pHead)
{assert(pHead);if (pHead->_next == pHead){printf("只有头节点,无法删除");return;}pHead->_prev->_prev->_next = pHead;ListNode* cur = pHead->_prev;pHead->_prev = pHead->_prev->_prev;free(cur);
}
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* ptr = (ListNode*)malloc(sizeof(ListNode));assert(ptr);ptr->_data = x;ptr->_prev = pHead;ptr->_next = pHead->_next;pHead->_next->_prev = ptr;pHead->_next = ptr;
}
// 双向链表头删
void ListPopFront(ListNode* pHead)
{assert(pHead);if (pHead->_next == pHead){printf("只有头节点,无法删除");return;}ListNode* cur = pHead->_next;pHead->_next = cur->_next;cur->_next->_prev = pHead;free(cur);
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{ListNode* cur = pHead->_next;while (cur != pHead){if (cur->_data == x){return cur;}cur = cur->_next;}
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{ListNode* ptr = (ListNode*)malloc(sizeof(ListNode));assert(ptr);ptr->_data = x;pos->_prev->_next = ptr;ptr->_prev = pos->_prev;ptr->_next = pos;pos->_prev = ptr;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{pos->_prev->_next = pos->_next;pos->_next->_prev = pos->_prev;free(pos);
}int main()
{ListNode* PH = ListCreate();ListPushBack(PH,1);ListPushBack(PH,2);ListPushBack(PH,3);ListPushBack(PH,4);ListPopBack(PH);ListPushFront(PH,0);ListPopFront(PH);ListInsert(ListFind(PH, 2),5);ListErase(ListFind(PH, 5));ListPrint(PH);
}