前言
经过两个多星期的链表学习(别问为什么学了这么久,问就是前段时间一不小心走进舒适圈效率极低),发了超多篇相关博客,链表的学习也差不多到尾声啦,于是在十二月的第一天,有了这篇双向链表。
因为其实带不带头结点循不循环差别不是很大,再加上本人犯懒,所以双向链表不会有带头结点和循环篇了(嘻~)
讲正事
其实双向链表在处理上和单链表最大的不同就是要兼顾一个结点里的两个指针,但是最常见的错误方式,就是在处理的时候会很自然地忘记处理指向前一个结点的指针。
准备工作——各种结构体的定义
结点
typedef struct node {
struct node* pre;
int num;
struct node* next;
}Node;
专用于储存链表信息的结构体
typedef struct linklist {
Node* head;
Node* tail;
}Linklist;
初始化
void Creat(Linklist* linklist) {
Node* p = (Node*)malloc(sizeof(Node));
printf("请输入第一个数据:");
scanf("%d", &p->num );
p->pre = NULL;
p->next = NULL;
linklist->head = p;
linklist->tail = p;
}
增
其实可以选择在中间某个地方或者链表头插入新结点,但是由于我懒得写,就只展示了尾插的方法
void add(Linklist* linklist) {
int flag = 0;
while (1) {
Node* p = (Node*)malloc(sizeof(Node));
printf("请输入要添加的数字:");
scanf("%d", &p->num);
p->next = NULL;
linklist->tail->next = p;//令前一个结点指向新结点
p->pre = linklist->tail;//令新结点反指前一个结点(!!双向才有的哦)
linklist->tail = p;//移动尾指针
printf("继续输入请按1,结束请按0:");
scanf("%d", &flag);
if (!flag) {
break;
}
}
}
删
在删除的时候,记得处理被删结点留下的痕迹!(断要断得干干净净才行哦)详见注释
void delete_sth(Linklist* linklist) {
int n = 0;
Node* p = linklist->head;
printf("请输入你要删除的数据:");
scanf("%d", &n);
while (p->num != n && p->next) {
p = p->next;
}
//这样子结束循环后,要么p停留在链表最后1个结点(不清楚是否找到数据),
//要么找到数据停留在要删结点(该结点位于链表中间或最前面)
if (p == linklist->head) {//先判断该结点是否为链表的第一个结点
linklist->head = p->next;
p->next->pre = NULL;//第二个结点的pre指向第一个结点,记得把这个关系抹掉
free(p);
}
else if (p->next) {//这种是找到了要删结点,且结点不是头尾的情况
p->pre->next = p->next;
p->next->pre = p->pre;
free(p);
}
else if (p->num == n) { //这种是要删结点是尾的情况
p->pre->next = NULL;//倒数第二个结点的next指向最后一个结点,记得也要抹掉这段关系
linklist->tail = p->pre;
free(p);
}
else { //这种是压根没找到数据
printf("未找到该数据\n");
}
}
改
就改个数据没啥好说的,不说了
void amend(Linklist* linklist) {
int n;
Node* p = linklist->head;
printf("请输入要修改的数字:");
scanf("%d", &n);
while (p) {
if (p->num != n) {
p = p->next;
}
else if (p->num == n) {
break;
}
}
if (!p) {
printf("您输入的编号不存在\n");
}
else {
printf("请输入改正后的数字:");
scanf("%d", &p->num);
}
}
逆置
逆置啥逆置,都双向了,正着能走反着也能走,这part跳过不逆了
遍历并输出链表
思想和单链表差不多,拿个工具指针从前往后指一个输出一个就行。
但是!!为了突出双向链表的特别,我特地从后面往前又遍历了一边(现在看我似乎有什么大病吃饱了撑的输出两遍)
void traversal(Linklist* linklist) {
Node* p = linklist->head;
//前一个往后一个遍历
printf("%d ", p->num);
while (p->next) {
p = p->next;
printf("%d ", p->num);
}
printf("\n");
//后一个往前一个遍历
printf("%d ", p->num);
while (p->pre) {
p = p->pre;
printf("%d ", p->num);
}
printf("\n");
}
特别栏目——总结
学完双向链表之后我发现这真是个好东西(果然双向奔赴是最美好的),从头可以开始从尾也可以开始,就是处理上有点麻烦,容易忘记处理和前一个的关系(所以说和前任断要断得干净以绝后患啊)。
最后的最后,十二月到啦,带着十一月的未完成,在十二月奔跑起来!即使道路泥泞,也会收获遍野的烂漫!
藏着许多节日的十二月,也要全力以赴的开心~