"Hello大家好,欢迎来到**【01二进制代码漫游日记】!**
今天我们的主题是C语言数据结构中的双向链表。我们将快速解锁它的核心操作:指定位置插入、删除节点、查找以及销毁链表。话不多说,直接上代码,一起玩转双向链表!"
"在正式动手写代码之前,我们先来认识一下今天项目的'三剑客'。一个结构清晰的项目,能让我们的思路事半功倍。我们一共准备了三个文件,它们各司其职,协同工作。"
一、文件定义及作用
1.1.List.h文件及作用
首先登场的是我们的'总设计师'------List.h头文件。它的作用非常关键,负责定义双向链表的'蓝图',也就是我们的节点结构体,同时也会声明所有函数的接口。其他文件想要和链表打交道,都得先包含它。
1.2.4-21.c文件及作用
接下来是我们的'实干家'------4-21.c实现文件。所有函数的具体逻辑和'硬功夫',比如插入、删除节点的具体代码,都会写在这里。它是整个项目的核心。
1.3.test.h文件及作用
最后是我们的'体验官'------test.h测试文件。它是程序的入口,也就是main函数所在的地方。我们会在这里演示如何使用前面定义好的所有功能,来验证我们的链表是否工作正常。
好了,认识了这三位'伙伴',"项目结构清晰了,万事俱备,只欠代码!那我们该从哪里下手呢?对于一个数据结构来说,'查找'是所有操作的基石。只有先学会了如何精准定位,我们才能对它进行后续的增删改。所以,我们的第一站,就从这里开始。
二、查找及其实现方法
2.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);
//查找
LTNode* LTFind(LTNode* phead, LTDataType x);
2.2.4-21.c部分

查找代码如下:
//查找
LTNode* LTFind(LTNode* phead, LTDataType x) {
assert(phead);
LTNode* pcur = phead->next;
while (pcur != phead) {
if (pcur->data == x) {
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
2.3.test.c部分

测试代码如下:
//查找
LTNode*pos=LTFind(plist, 1);
if (pos != NULL) {
printf("找到了,是%d\n",pos->data);
}
else {
printf("没有找到,请重新输入:");
}
}
测试结果如下:


"查找是为了定位,而定位往往是为了后续的修改做准备。搞定了'在哪里'的问题,接下来我们看看'怎么做'------如何在找到的这个位置之后,插入新的节点
三、在指定位置之后插入数据
3.1.List.h部分

代码如下:
//在指定位置之后插入新结点
void LTInsert(LTNode* phead, LTDataType x);
3.2.4-21.c部分

代码如下:
//在指定位置之后插入新结点
void LTInsert(LTNode* pos, LTDataType x) {
assert(pos);
LTNode* newnode = LTBuyNode(x);
newnode->next = pos->next;
newnode->prev = pos;
pos->next->prev = newnode;
pos->next = newnode;
}
3.3.test.c部分

代码如下:
//在指定位置之后插入新结点
LTNode* pos2 = LTFind(plist, 2);
if (pos2) {
LTInsert(pos2, 8);
}
LTNode* pos4 = LTFind(plist, 9);
if (pos4) {
LTInsert(pos4, 9);
}
LTPrint(plist);
测试结果如下:

"在动态的数据世界里,有增就有减。我们刚刚攻克了'在指定位置之后插入数据'的难题,让链表变得灵活多变。但光会'增'还不够,当某个节点的数据过时或无效时,我们需要一种机制来'修剪'它。接下来,我们就来探讨如何删除指定位置的节点。"
四、删除pos(指定位置)节点
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);
//查找
LTNode* LTFind(LTNode* phead, LTDataType x);
//在指定位置之后插入新结点
void LTInsert(LTNode* pos, LTDataType x);
//删除指定位置结点
void LTErase(LTNode* pos);
4.2. 4-21.c部分

代码如下:
//删除指定位置结点
void LTErase(LTNode* pos) {
assert(pos);
pos->next->prev = pos->prev;
pos->prev->next = pos->next; \
free(pos);
pos = NULL;
}
4.3.test.c部分

代码如下:
//删除指定位置结点
LTNode* pos = LTFind(plist, 9);
if (pos != NULL) {
LTErase(pos);
}
LTPrint(plist);
测试结果如下:

"我们刚刚学会了如何精准地删除单个节点,就像处理掉一个废弃的零件。但在程序的世界里,当我们彻底完成一个任务,不再需要整个数据结构时,就不能只删除某一个节点了。我们需要一种'大扫除'式的操作,一次性释放所有节点,将内存归还给系统。这不仅是一种操作,更是一种编程的责任。下面,让我们来完成链表的销毁。"
五、销毁链表
5.1List.h部分

代码如下:
//销毁链表
void LTDestroy(LTNode* phead);
5.2. 4-21.c部分

代码如下:
//销毁链表
void LTDestroy(LTNode* phead) {
assert(phead);
LTNode* pcur = phead->next;
while (pcur != phead) {
LTNode* next = pcur->next;
free(pcur);
pcur = next;
}
free(phead);
phead = NULL;
}
5.3.test.c部分

代码如下:
LTPrint(plist);
//销毁链表
LTDestroy(plist);
LTPrint(plist);
plist = NULL;
}
"OK,双向链表的关键操作就介绍到这里。希望大家通过今天的学习,能轻松掌握它!
如果觉得有用**,别忘了关注我,**获取更多编程技巧和实战经验。你的支持是我更新的动力!我们下期见!"
