Hello,大家好呀! 欢迎来到【01二进制代码漫游日记】。
在C语言的浩瀚宇宙中,数据结构绝对是那颗最耀眼的恒星。今天,我们要一起解锁的,是数据结构中极其灵活、却又让不少人头疼的------双向链表。
别担心,这篇内容不仅仅是枯燥的代码堆砌。我会手把手带你理清它的逻辑,从最基础的头插法 到复杂的节点删除,一步步拆解。在正式敲代码之前,我们先来认识一下今天的"作战地图",看看各个文件都扮演着什么角色......
一、文件定义及作用
1.1.4-21.c文件及作用
实现文件,作用是包含函数的具体逻辑代码
1.2.List.h文件及作用
头文件,作用是定义结构体 和所有函数的接口。
1.3.test.h文件及作用
测试文件,作用是程序的入口,演示如何使用上述所有功能。
二、头插及其实现方式
2.1.List.h(头文件)

完整代码如下:
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct LTNode {
LTDataType data;
struct LTNode* prev;
struct LTNode* next;
}LTNode;
//申请节点
LTNode* LTBuyNode(LTDataType x);
void LTInit(LTNode** pphead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPrint(LTNode* phead);
void LTPushFront(LTNode* phead, LTDataType x);
2.2.4-21.c(实现文件)

完整代码如下:
void LTPushFront(LTNode* phead, LTDataType x) {
LTNode* newnode = LTBuyNode(x);
assert(phead);
newnode->prev = phead;
newnode->next = phead->next;
phead->next->prev = newnode;
phead->next = newnode;
}
2.3.test.c(实现文件)
完整代码如下:
LTPushFront(plist, 5);
LTPrint(plist);
LTPushFront(plist, 6);
LTPrint(plist);
LTPushFront(plist, 7);
LTPrint(plist);
LTPushFront(plist, 8);
LTPrint(plist);
测试结果如下:

三、尾删及其实现方式
3.1.List.h(头文件)

完整代码如下:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct LTNode {
LTDataType data;
struct LTNode* prev;
struct LTNode* next;
}LTNode;
//申请节点
LTNode* LTBuyNode(LTDataType x);
void LTInit(LTNode** pphead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPrint(LTNode* phead);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
3.2.4-21.c(实现文件)

完整代码如下:
//尾删
void LTPopBack(LTNode* phead) {
assert(phead && phead->next != NULL);
LTNode*del = phead->prev;
del->prev->next = phead;
phead->prev = del->prev;
free(del);
del = NULL;
}
3.3.test.c(实现文件)

完整代码如下:
LTPopBack(plist);
LTPrint(plist);
LTPopBack(plist);
LTPrint(plist);
LTPopBack(plist);
LTPrint(plist);
测试结果如下:
四、头删及其实现方式
4.1.List.h(头文件)

完整代码如下:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct LTNode {
LTDataType data;
struct LTNode* prev;
struct LTNode* next;
}LTNode;
//申请节点
LTNode* LTBuyNode(LTDataType x);
void LTInit(LTNode** pphead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPrint(LTNode* phead);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
//头删
void LTPopFront(LTNode* phead);
4.2.4-21.c(实现文件)

完整代码如下:
//头删
void LTPopFront(LTNode* phead) {
assert(phead && phead->next != phead);
LTNode* del = phead->next;
phead->next = del->next;
del->next->prev = phead;
free(del);
del = NULL;
}
4.3.test.c(实现文件)
代码如下:
LTPopFront(plist);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
测试结果如下:

好了,关于双向链表的"头插"与"删除"操作 ,我们就先聊到这里。
写链表就像是在编织一张网,虽然指针的操作有时会让人晕头转向,但当你理清了节点之间的前后关系,那种逻辑闭环的快感是无与伦比的。
如果你觉得这篇笔记帮你避开了指针的"坑",或者让你对双向链表有了新的理解,请一定不要忘记点个关注!
后续我还会继续更新更多C语言的高级玩法和数据结构实战,关注我,让我们一起在01的世界里,把代码写得更优雅、更从容!我们下期见!
