目录
前言
之前的文章我们讲解了顺序表和单链表,这篇我们来一起研究一下双向链表作为链表的收尾
双向链表简介
双向链表也是一种链式数据结构,每个节点包含两个指针:
前驱指针:指向当前节点的前一个节点
后继指针:指向当前节点的后一个节点
相比于单向链表,双向链表支持双向遍历,删除指定节点更高效,插入操作更灵活。但也有自己的缺点。每个节点多消耗一个指针的存储空间,操作逻辑更复杂,需同时维护两个指针
代码解析
我们依旧研究双向链表的一些常见功能增强理解
1.插入数据
LTNode* BuyListNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
node->next = NULL;
node->prev = NULL;
node->data = x;
return node;
}
开创一块空间存储新元素,注意双向链表有前驱指针和后驱指针,要进行初始化养成良好的代码习惯。
2.打印
void LTPrint(LTNode* phead)
{
printf("<=head=>");
LTNode* cur = phead->next;
while (cur != phead) {
printf("%d<=>", cur->data);
cur = cur->next;
printf("\n");
}
我们派一个检查官cur来遍历整个链表,打印每个节点的数据元素,当结束一轮回到头结点时,完成打印
3.万能插入
void LTInsert(LTNode* pos, LTDataType x)
{
LTNode* prev = pos->prev;
LTNode* newnode = BuyListNode(x);
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
逻辑如图

通过这个插入我们只需传入正确的节点位置就可以轻而易举的完成尾插和头插操作啦
void LTPushBack(LTNode* phead, LTDataType x)
{
LTInsert(phead, x);
}//尾部插入
void LTPushFront(LTNode* phead, LTDataType x)
{
LTInsert(phead->next, x);
}//头部插入
4.尾删的实现
void LTPopBack(LTNode* phead)
{
LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
tailPrev->next = phead;
phead->prev = tailPrev;
free(tail);
}
通过tail找到倒数第二个节点指向头结点即可实现尾删操作。
小结
至此链表和顺序表我们已经初步掌握,让我们再来梳理总结一下吧.
相同点:均为线性表,支持数据元素的逻辑顺序存储。
不同点:
访问效率:顺序表 > 链表
增删效率:链表 > 顺序表
内存管理:顺序表需预分配,链表按需分配
每日一题
链表的中间节点

代码实现
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode *slow = head, *fast = head;
while (fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
利用双指针一快一慢同时从头结点开始移动,慢指针每次移动一步,快指针每次移动两步
当链表奇数长度时,fast最终指向尾节点(fast->next == NULL)
当链表偶数长度时,fast最终指向null
通过分类实现slow最终刚好指向预定的中间位置
以上就是本篇全部内容啦,有任何不足欢迎大家在评论区交流指正。

3万+

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



