vlambda博客
学习文章列表

【C语言教程】双向链表学习总结和C语言代码实现!值得学习~

双向链表

定义

我们一开始学习的链表中各节点中都只包含一个指针(游标),且都统一指向直接后继节点,通常称这类链表为单向链表。

虽然使用单向链表能 100% 解决逻辑关系为 "一对一" 数据的存储问题,但在解决某些特殊问题时,单链表并不是效率最优的存储结构。比如说,如果算法中需要大量地找某指定节点的前驱节点,使用单链表无疑是灾难性的,因为单链表更适合 "从前往后" 找,而 "从后往前" 找并不是它的强项。

为了能够高效率解决类似的问题,就发明了双向链表。从名字上理解双向链表,即链表是 "双向" 的,如下图所示:


从上图中可以看到,双向链表中各节点包含以下 3 部分信息(如下图所示):

●  指针域 prior:用于指向当前节点的直接前驱节点;

●  数据域 data:用于存储数据元素。

●  指针域 next:用于指向当前节点的直接后继节点;

【C语言教程】双向链表学习总结和C语言代码实现!值得学习~

因此,双链表的节点结构用 C 语言实现为:

typedef struct Node { struct Node *prior;//指向直接前驱节点 ElemType data;//数据域 struct Node *next;//指向直接后继节点} Node;

注意:因为带头节点会更好操作,所以我的代码都有头节点。


1、双向链表的创建

同单链表相比,双链表仅是各节点多了一个用于指向直接前驱的指针域。因此,我们可以在单链表的基础轻松实现对双链表的创建。

//1、初始化双向链表(带头节点)Status initLinkList(LinkList *list){ //创建头节点 *list = malloc(sizeof(Node)); if (*list == NULL) { return ERROR; } (*list)->prior = NULL; (*list)->data = -1; (*list)->next = NULL; printf("已初始化链表~\n"); return OK;}


2、遍历双向链表

和单向链表遍历方式一模模一样样,这里就不多讲。我多加了一层使用prior指针逆序输出,相信有点基础的同学应该能一眼看明白。

//2、遍历双向链表void printfLinkLisk(LinkList list){ printf("遍历链表:\n"); if (list == NULL || list->next == NULL) { printf("这是一个空链表\n"); return; } LinkList p = list; //判断next是否全部正确 printf("根据next从前往后遍历:"); while (p->next) { printf("%d ",p->next->data); p = p->next; } printf("\n"); //判断prior是否全部正确 printf("根据prior从后往前遍历:"); while (p != list) { printf("%d ",p->data); p = p->prior; } printf("\n");}


3、根据索引位置添加节点

因为我的双向链表有头节点,所以只有两种添加情况:

 ●  添加至表的中间位置

同单链表添加数据类似,双向链表中间位置添加数据需要经过以下 4 个步骤(步骤中的顺序中 3 必须放到 1 和 2 后面,其它顺序可变),如下图所示:

● 将priorNode->next节点的prior指向新节点;

● 将新节点->next指向原来的priorNode->next;

● 将priorNode->next指向新节点;

● 新节点的prior指向priorNode。

【C语言教程】双向链表学习总结和C语言代码实现!值得学习~


● 添加至表尾

与添加到表中间的步骤只需要少掉步骤 1。因为priorNode->next是Null,不能用它执行操作,否则会崩溃。

//3、根据索引位置插入数据至链表中Status insertLinkList(LinkList *list, int index, ElemType data){ if (list == NULL || index < 0) { return ERROR; } int i = 0; LinkList priorNode = *list; //判断插入的位置,这里开始位置是0,index超过链表长度则插入末尾 while (i < index && priorNode->next != NULL) { priorNode = priorNode->next; i++; } LinkList newNode = malloc(sizeof(Node)); if (newNode == NULL) { return ERROR; } newNode->data = data; //插入操作共四步,看好了,别眨眼 //1.将priorNode->next节点的前驱指向新节点 if (priorNode->next) { priorNode->next->prior = newNode; } //2.将新节点->next指向原来的priorNode->next newNode->next = priorNode->next; //3.将priorNode->next指向新节点 priorNode->next = newNode; //4.新节点的前驱指向priorNode newNode->prior = priorNode; return OK;}



4、根据索引位置删除节点

根据索引删除节点时,只需遍历链表找到要删除的结点,更改前驱节点的next和后继节点的prior即可。

【C语言教程】双向链表学习总结和C语言代码实现!值得学习~

//4、根据索引位置删除节点Status deleteLinkListByIndex(LinkList *list, int index, ElemType *data){ if (*list == NULL || index < 0) { return ERROR; } LinkList locaNode = *list; int i = 0; while (i <= index) { locaNode = locaNode->next; if (locaNode == NULL) { printf("没有这个你想要删除的节点\n"); return ERROR; } i++; } //开始删除,只需要做两步 //1、更改前驱节点的next locaNode->prior->next = locaNode->next; //2、更改后继节点的prior。 if (locaNode->next) { locaNode->next->prior = locaNode->prior; } *data = locaNode->data; free(locaNode); return OK;}


5、根据存储的值删除节点

根据值删除节点时,只需遍历链表找到要删除的结点,更改前驱节点的next和后继节点的prior即可。

//5、根据存储的值删除节点Status deleteLinkListByData(LinkList *list, ElemType data){

if (*list == NULL) { return ERROR; } LinkList locaNode = (*list)->next; while (locaNode) { if (locaNode->data == data) { break; } locaNode = locaNode->next; } if (locaNode == NULL) { printf("没有这个你想要删除的节点\n"); return ERROR; } //开始删除,只需要做两步 locaNode->prior->next = locaNode->next; if (locaNode->next) { locaNode->next->prior = locaNode->prior; } free(locaNode); return OK;}

6、根据值查找节点

方法同单向链表

//6、查找元素Status selectNode(LinkList list, ElemType data, LinkList *locaNode){

if (list == NULL) { return ERROR; } LinkList p = list->next; while (p) { if (p->data == data) { *locaNode = p; break; } p = p->next; } if (*locaNode == NULL) { printf("没有这个你想要的节点\n"); return ERROR; } else { return OK; }}

其它辅助代码

#include "stdlib.h"#define OK 1#define ERROR 0//元素类型typedef int ElemType;//状态类型typedef int Status;//定义节点结构体typedef struct Node { struct Node *prior; ElemType data; struct Node *next;} Node;typedef Node *LinkList;int main(int argc, const char * argv[]) { LinkList list; initLinkList(&list); for (int i = 0; i < 10; i ++) { insertLinkList(&list, i, i); } printfLinkLisk(list); int index, data; printf("输入你想插入的位置(从0开始)和存储的值:"); scanf("%d %d",&index,&data); insertLinkList(&list, index, data); printfLinkLisk(list); printf("输入你想删除的位置(从0开始):"); scanf("%d",&index); deleteLinkListByIndex(&list, index, &data); printfLinkLisk(list); printf("输入你想删除的节点的值(只删最前的那个):"); scanf("%d",&data); deleteLinkListByData(&list, data); printfLinkLisk(list); printf("\n"); return 0;}

输出结果:

【C语言教程】双向链表学习总结和C语言代码实现!值得学习~

看到这里是不是又学到了很多新知识呢~

如果你很想学编程,小编推荐我的C语言/C++编程学习基地→

【Q群:1083154082】!

都是学编程小伙伴们,带你入个门还是简简单单啦,一起学习,一起加油~

还有许多学习资料和视频,相信你会喜欢的!

涉及:游戏开发、常用软件开发、编程基础知识、课程设计、黑客等等......