mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
958 字
2 分钟
双向链表实现与应用

双向链表#

前几篇文章,我们大致了解了链表是个什么东西,他有许多节点,节点里一个用于存储数据,一个用于存储下一个节点的地址。

这样的链表也叫做单链表。

而本文将介绍一下双向链表😎

介绍#

双向链表,顾名思义,就是有两个方向,一个连接到上一个节点,另一个链接到下一个节点。

在c语言中,我们定义单链表是如下的。

struct Node{
int data;
struct Node *next;
}

那么双向链表呢?实现起来也很简单。

struct Node{
int data;
struct Node *next;
struct Node *prev;
}

效果如下图

a

双向链表的优势与缺点#

  • 如果我们有指向任何节点的指针,那么我们可以进行反向查询。

b

比如这张图,我们这里显示一个名为temp的指针,那么我们可以访问temp,temp->next,temp->prev

而在单向链表里面,我们是无法通过一个指针来查询上一个节点的!😎你必须再用一个指针跟踪上一个节点!

  • 删除节点更加方便
  • 反转查询方便

缺点就是我们必须为上一个节点的指针使用额外的内存。还有就是在插入和删除时要操作更多的链接!,因此更容易出错!🤔

实现#

假设我们有一个如下的链表

q

我们要实现,在头部插入节点,在尾部插入节点,打印函数,反转打印函数。

1.首先实现插头函数#

void InsertAtHead(int x){
//插在头部
struct Node myNode;
myNode.data=x;
myNode.next=NULL;
myNode.prev=NULL;
}

你觉得上面写的对吗?🤔

那肯定错了呀! struct Node myNode;这里采用的是局部变量,只存在于栈空间,函数结束后就会被删除🤡

void InsertAtHead(int x){
//插在头部
struct Node *newNode=(struct Node*)malloc(sizeof(struct Node));
newNode->data=x;
newNode->next=NULL;
newNode->prev=NULL;
}

这样在堆里面申请内存才是正确的,还记得我们之前说过要操作堆里面的内存必须通过指针吗😎

由于创建一个新节点很常用,所以我们把他从这个函数中剥离出来!

struct Node *GetNewNode(int x){
struct Node *newNode=(struct Node*)malloc(sizeof(struct Node));
newNode->data=x;
newNode->next=NULL;
newNode->prev=NULL;
return newNode;
}

接着我们再来实现插入

void InsertAtHead(int x){
struct Node *newNode=GetNewNode(x);
if(head==NULL){
//处理空节点的情况
head = newNode;
return;
}
head->prev=newNode;
newNode->next=head;
head=newNode;
}

处理空节点的情况就不画图了(懒)🤡

假设我们要在2前面插入一个4,如图(上面400地址的next应该是0,下面600地址左右均为0,这里笔误了)。

c

插入函数会将他们变成这样

d

简化成如下

e

如果你问newNode指针跑哪去了?那说明你还是没有搞懂栈和堆的内存分布,建议补一下os的知识🤔

newNode是函数里的局部变量,也就是说只要函数完成,它就会被一起清出内存,所以这里才没画。

void InsertAtHead(int x){
//插在头部
struct Node myNode;//写成struct Node *myNode;那么第7行就可以改写成return myNode;
myNode.data=x;
myNode.next=NULL;
myNode.prev=NULL;
return &myNode;
}

那就拓展一下吧,如果之前的函数写成这样会发生什么?

假设我们创建了一个地址为50的新节点,注意这个节点的内存地址在stack(栈)里面!

所以一但函数完成,那么栈帧将会被回收!所以即使你有了50的地址,但是那片地址里的东西全被回收了,所以会变成空空如也🤣

这就是为什么我们需要在堆上申请内存。

struct Node *newNode=(struct Node*)malloc(sizeof(struct Node));

struct Node *newNode在栈上申请了个地址用于存放指针,来操作堆里面的内容!。

(struct Node*)malloc(sizeof(struct Node));在堆里面申请内存!

当函数结束时,我们会获得堆里面的地址,也就是那个指针所指的地址,这是不会被自动回收的!

2.实现打印#

打印和单链表没什么区别

void Print(){
struct Node* temp=head;
printf("forward:");
while(temp!=NULL){
printf("%d",temp->data);
temp=temp->next;
}
printf("\n");
}

3.实现反转打印#

void ReversePrint(){
struct Node *temp=head;
if(temp==NULL)return;//如果是空链表直接退出
while(temp->next!=NULL){
temp=temp->next;
}
printf("reverse:");
while(temp!=NULL){
printf("%d",temp->data);
temp=temp->prev;
}
printf("\n");
}

这个比单链表实现简单多了吧!

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

双向链表实现与应用
https://fatdog.20060113.xyz/posts/double-linklist/
作者
神秘大胖狗
发布于
2026-04-05
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00