【C 数据结构】双向链表

文章目录

  • 【 1. 基本原理 】
  • 【 2. 双向链表的 创建 】
    • 实例 - 输出双向链表
  • 【 3. 双向链表 添加节点 】
  • 【 4. 双向链表 删除节点 】
  • 【 5. 双向链表查找节点 】
  • 【 7. 双向链表更改节点 】
  • 【 8. 实例 - 双向链表的 增删查改 】

【 1. 基本原理 】

  • 表中各节点中都只包含一个指针(游标),且都统一指向直接后继节点,通常称这类链表为 单向链表(或单链表)。
  • 背景
    如果算法中需要大量地找某指定结点的前趋结点,使用单链表无疑是灾难性的,因为单链表更适合 “从前往后” 找,而 “从后往前” 找并不是它的强项。为了能够高效率解决类似的问题,引入双向链表(简称双链表)。
  • 从名字上理解 双向链表,即链表是 “双向” 的,(双向指的是各节点之间的逻辑关系是双向的) 每个节点存在前后两个指针,分别指向前驱节点和后继节点,但通常头指针只设置一个,除非实际情况需要。
    在这里插入图片描述
  • 双向链表中各节点包含以下 3 部分信息:
    • 前指针域:用于指向当前节点的直接前驱节点;
    • 数据域:用于存储数据元素。
    • 后指针域:用于指向当前节点的直接后继节点;
      在这里插入图片描述
  • 双链表的节点结构用 C 语言实现为:
typedef struct line
{struct line * prior; //指向直接前趋int data;struct line * next; //指向直接后继
}line;

【 2. 双向链表的 创建 】

  • 同单链表相比,双链表仅是各节点多了一个用于指向直接前驱的指针域。因此,我们可以在单链表的基础轻松实现对双链表的创建。
  • 需要注意的是,与单链表不同,双链表创建过程中,每创建一个新节点,都要与其前驱节点建立两次联系,分别是:
    • 将新节点的 prior 指针指向直接前驱节点;
    • 将直接前驱节点的 next 指针指向新节点;
  • 创建双向链表的 C 语言实现代码:
//双链表的创建函数
line* initLine(line* head,int x[],int N)
{//创建一个首元节点,链表的头指针为headhead = (line*)malloc(sizeof(line));//对首元节点进行初始化head->prior = NULL;head->next = NULL;head->data = x[0];//声明一个指向尾巴节点的指针,方便后期向链表中添加新创建的节点line* list = head;for (int i = 1; i <N; i++){//创建新的节点并初始化line* body = (line*)malloc(sizeof(line));body->prior = NULL;body->next = NULL;body->data = x[i];//新节点与链表最后一个节点建立关系list->next = body;body->prior = list;//list永远指向链表中最后一个节点list = list->next;}//返回新创建的链表return head;
}

实例 - 输出双向链表

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>//双向链表结构体
typedef struct line
{struct line* prior;int data;struct line* next;
}line;//双链表的创建函数
line* initLine(line* head,int x[],int N)
{//创建一个首元节点,链表的头指针为headhead = (line*)malloc(sizeof(line));//对首元节点进行初始化head->prior = NULL;head->next = NULL;head->data = x[0];//声明一个指向尾巴节点的指针,方便后期向链表中添加新创建的节点line* list = head;for (int i = 1; i <N; i++){//创建新的节点并初始化line* body = (line*)malloc(sizeof(line));body->prior = NULL;body->next = NULL;body->data = x[i];//新节点与链表最后一个节点建立关系list->next = body;body->prior = list;//list永远指向链表中最后一个节点list = list->next;}//返回新创建的链表return head;
}//输出双链表的函数
void display(line* head)
{line* temp = head;while (temp){//如果该节点无后继节点,说明此节点是链表的最后一个节点if (temp->next == NULL)printf("%d\n", temp->data);elseprintf("%d <-> ", temp->data);temp = temp->next;}
}int main()
{int N;printf("请输入链表的数量:");scanf("%d",&N);printf("请输入链表的元素:");int* x = new int[N];for (int i = 0; i < N; ++i)scanf("%d", &x[i]);//创建一个头指针line* head = NULL;//调用链表创建函数head = initLine(head,x,N);//输出创建好的链表display(head);//显示双链表的优点printf("链表中第 4 个节点的直接前驱是:%d", head->next->next->next->prior->data);return 0;
}

在这里插入图片描述

【 3. 双向链表 添加节点 】

  • 添加至表头
    将新数据元素添加到表头,只需要将该元素与表头元素建立双层逻辑关系即可。换句话说,假设新元素节点为 temp,表头节点为 head,则需要做以下 2 步操作即可:
    • 新节点与头节点连接:temp->next=head; head->prior=temp;
    • head指向新节点:将 head 移至 temp,重新指向新的表头;

例如,将新元素 7 添加至双链表的表头,则实现过程如图 2 所示:
在这里插入图片描述

  • 添加至表的中间位置
    同单链表添加数据类似,双向链表中间位置添加数据需要经过以下 2 个步骤,如下图所示:
    • 新节点先与其直接后继节点建立双层逻辑关系;
    • 新节点的直接前驱节点与之建立双层逻辑关系;
      在这里插入图片描述
  • 添加至表尾
    与添加到表头是一个道理,更简单,实现过程如下:
    • 找到双链表中最后一个节点;
  • 让新节点与最后一个节点进行双层逻辑关系;
    在这里插入图片描述
  • C 语言实现
line * insertLine(line * head,int data,int add)
{//新建数据域为data的结点line * temp=(line*)malloc(sizeof(line));temp->data=data;temp->prior=NULL;temp->next=NULL;//插入到链表头,要特殊考虑if (add==1) {temp->next=head;head->prior=temp;head=temp;}else{line * body=head;//找到要插入位置的前一个结点bodyfor (int i=1; i<add-1; i++) {body=body->next;}//判断条件为真,说明插入位置为链表尾if (body->next==NULL) {body->next=temp;temp->prior=body;}else{body->next->prior=temp;//新节点后1个节点的前向指针指向新节点temp->next=body->next;//新节点的后向指针指向后一个节点body->next=temp;//新节点前1个节点的后向指针指向新节点temp->prior=body;//新节点的前向指针指向前一个节点}}return head;
}

【 4. 双向链表 删除节点 】

-双链表删除结点时,只需遍历链表找到要删除的结点,然后将该节点从表中摘除即可。

  • 删除元素 2 的操作过程,如下图所示:
    在这里插入图片描述
  • 双向链表删除节点的 C 语言实现代码如下:
line* delLine(line* head, int data)
{line* temp = head;//遍历链表while (temp){if (temp->prior == NULL && temp->data == data)//删除头节点{temp->next->prior = NULL;return head->next;}else if (temp->next == NULL && temp->data == data) //删除尾节点{temp->prior->next = NULL;return head;}else if (temp->data == data){temp->prior->next = temp->next;temp->next->prior = temp->prior;free(temp);return head;}temp = temp->next;}printf("链表中无该数据元素");return head;
}

【 5. 双向链表查找节点 】

  • 通常,双向链表同单链表一样,都仅有一个头指针。因此,双链表查找指定元素 的实现同单链表类似,都是 从表头依次遍历表中元素
  • C 语言实现代码为:
//head为原双链表,elem表示被查找元素
int selectElem(line * head,int elem)
{
//新建一个指针t,初始化为头指针 headline * t=head;int i=1;while (t) {if (t->data==elem) {return i;}i++;t=t->next;}//程序执行至此处,表示查找失败return -1;
}

【 7. 双向链表更改节点 】

  • 更改双链表中指定结点数据域的操作是在查找的基础上完成的。实现过程是:通过遍历找到存储有该数据元素的结点,直接更改其数据域即可
  • 实现此操作的 C 语言实现代码如下:
//更新函数,其中,add 表示更改结点在双链表中的位置,newElem 为新数据的值
line *amendElem(line * p,int add,int newElem)
{line * temp=p;//遍历到被删除结点for (int i=1; i<add; i++) {temp=temp->next;}temp->data=newElem;return p;
}

【 8. 实例 - 双向链表的 增删查改 】

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
//双向链表结构体
typedef struct line
{struct line* prior;int data;struct line* next;
}line;
//双链表的创建
line* initLine(line* head,int x[],int N);
//双链表插入元素,add表示插入位置
line* insertLine(line* head, int data, int add);
//双链表删除指定元素
line* delLine(line* head, int data);
//双链表中查找指定元素
int selectElem(line* head, int elem);
//双链表中更改指定位置节点中存储的数据,add表示更改位置
line* amendElem(line* p, int add, int newElem);
//输出双链表的实现函数
void display(line* head);int main()
{int x[5] = {1,3,5,7,9};//创建一个头指针line* head = NULL;//调用链表创建函数head = initLine(head, x, 5);display(head);printf("在表中第 3 的位置插入数据 66 后:\n");head = insertLine(head, 66, 3);display(head);//表中删除元素9printf("表中删除元素 9后:\n");head = delLine(head, 9);display(head);printf("元素 3 的位置是:%d\n", selectElem(head, 3));//表中第 3 个节点中的数据改为存储 8printf("表中第 3 个节点中的数据改为数据 8后:\n");head = amendElem(head, 3, 8);display(head);return 0;
}//双链表的创建函数
line* initLine(line* head, int x[], int N)
{//创建一个首元节点,链表的头指针为headhead = (line*)malloc(sizeof(line));//对首元节点进行初始化head->prior = NULL;head->next = NULL;head->data = x[0];//声明一个指向尾巴节点的指针,方便后期向链表中添加新创建的节点line* list = head;for (int i = 1; i < N; i++){//创建新的节点并初始化line* body = (line*)malloc(sizeof(line));body->prior = NULL;body->next = NULL;body->data = x[i];//新节点与链表最后一个节点建立关系list->next = body;body->prior = list;//list永远指向链表中最后一个节点list = list->next;}//返回新创建的链表return head;
}line* insertLine(line* head, int data, int add)
{//新建数据域为data的结点line* temp = (line*)malloc(sizeof(line));temp->data = data;temp->prior = NULL;temp->next = NULL;//插入到链表头,要特殊考虑if (add == 1){temp->next = head;head->prior = temp;head = temp;}else{line* body = head;//找到要插入位置的前一个结点for (int i = 1; i < add - 1; i++){body = body->next;}//判断条件为真,说明插入位置为链表尾if (body->next == NULL){body->next = temp;temp->prior = body;}else{body->next->prior = temp;temp->next = body->next;body->next = temp;temp->prior = body;}}return head;
}
line* delLine(line* head, int data)
{line* temp = head;//遍历链表while (temp){if (temp->prior == NULL && temp->data == data)//删除头节点{temp->next->prior = NULL;return head->next;}else if (temp->next == NULL && temp->data == data) //删除尾节点{temp->prior->next = NULL;return head;}else if (temp->data == data){temp->prior->next = temp->next;temp->next->prior = temp->prior;free(temp);return head;}temp = temp->next;}printf("链表中无该数据元素");return head;
}
//head为原双链表,elem表示被查找元素
int selectElem(line* head, int elem)
{//新建一个指针t,初始化为头指针 headline* t = head;int i = 1;while (t){if (t->data == elem){return i;}i++;t = t->next;}//程序执行至此处,表示查找失败return -1;
}
//更新函数,其中,add 表示更改结点在双链表中的位置,newElem 为新数据的值
line* amendElem(line* p, int add, int newElem)
{line* temp = p;//遍历到被删除结点for (int i = 1; i < add; i++){temp = temp->next;}temp->data = newElem;return p;
}
//输出链表的功能函数
void display(line* head)
{line* temp = head;while (temp){if (temp->next == NULL){printf("%d\n", temp->data);}else{printf("%d->", temp->data);}temp = temp->next;}
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/309531.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在线课程平台LearnDash评测 – 最佳 WordPress LMS插件

在我的LearnDash评测中&#xff0c;我探索了流行的 WordPress LMS 插件&#xff0c;该插件以其用户友好的拖放课程构建器而闻名。我深入研究了各种功能&#xff0c;包括课程创建、测验、作业、滴灌内容、焦点模式、报告、分析和管理工具。 我的评测还讨论了套餐和定价选项&…

链表创建的陷阱与细节

链表是线性表的一种&#xff0c;它在逻辑结构上是连续的&#xff0c;在物理结构上是非连续的。 也就是说链表在物理空间上是独立的&#xff0c;可能是东一块西一块的。如下顺序表和链表在内存空间上的对比&#xff1a; 而链表的每一块空间是如何产生联系实现在逻辑结构上是连续…

欢乐钓鱼大师秒杀源码

gg修改器设置里面单选a内存然后去试试e类型搜索鱼竿的拉杆速度然后点修改点很多增加1然后游戏返回在进去看鱼竿拉速然后在修改器的里面找到拉速一样的数值其他恢复全移除不恢复移除会闪退然后点开保留下来的拉速数值点转到会有一堆数值你得找里面找到鱼竿的伤害距离等数值就可以…

Linux内核常见的丢包场景有哪些

目录 摘要 1 收发包处理流程 2 硬件网卡相关 2.1 ring buffer满 2.2 利用 ntuple 保证关键业务 3 arp丢包 3.1 neighbor table overflow 3.2 unresolved drops 4 conntrack丢包&#xff1a;nf_conntrack: table full 5 udp接收buffer满 6 丢包定位 6.1 dropwatch 查看丢包 6.2…

线程池与工厂模式

线程池 如果我们需要频繁的创建销毁线程,此时创建销毁线程的成本,就不能忽视了.因此就可以使用线程池.即,提前搞好一波线程,后续需要使用线程就直接从池子里拿一个即可.当线程不再使用,就放回池子里. 本来,是需要创建线程/销毁线程.现在是从池子里获取到现成的线程,并且把线程…

BackTrader 中文文档(十)

原文&#xff1a;www.backtrader.com/ 用户自定义佣金 原文&#xff1a;www.backtrader.com/docu/user-defined-commissions/commission-schemes-subclassing/ 重塑 CommInfo 对象到实际形式的最重要部分涉及&#xff1a; 保留原始的 CommissionInfo 类和行为 为轻松创建用户定…

前端三剑客 —— JavaScript (第六节)

目录 内容回顾 BOM编程 DOM编程* document对象 document对象的属性 document对象的方法 DOM对象节点 操作DOM对象内容 操作DOM对象的属性 --- DOM对象.属性名称 --- DOM对象[属性名称] --- 调用系统API &#xff08;Application Program interface&#xff09;&#…

行业模板|DataEase批发零售大屏模板推荐

DataEase开源数据可视化分析平台于2022年6月发布模板市场&#xff08;https://templates-de.fit2cloud.com&#xff09;&#xff0c;并于2024年1月新增适用于DataEase v2版本的模板分类。模板市场旨在为DataEase用户提供专业、美观、拿来即用的大屏模板&#xff0c;方便用户根据…

2024 年江苏省职业院校技能大赛“区块链技术应用” 赛项赛卷(样卷)运维题解析一

运维题 环境: ubuntu20 fisco 2.8.0 前言 准备两台机子,并且可以能相互pin通 192.168.19.133 [M1-A] 192.168.19.137 [M2-B] 子任务 1-2-1: 搭建区块链系统并验证 基于给定服务器环境以及软件,搭建一条双机 1 机构 8 节点 1 群组的区块 链系统(默认端口开始[30300,2020…

最优算法100例之43-包含min函数的栈

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 题目描述: 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数,在该栈中,调用min,push及pop的时间复杂…

idea 中运行spring boot 项目报 Command line is too long的解决办法。

Command line is too long 在这里选择edit configures 选择shrten command line , 选择 jar manifest 运行即可。

5G网络开通与调测ipv4

要求如下&#xff1a; 1. 勘站规划 1. 【重】首先观察NR频点&#xff0c;完成设备选型 2645--选择N41 3455--选择N78 4725--选择N79 设备选型如下&#xff1a;观察AAU的通道数&#xff0c;最大发射功率&#xff1b;选择N41的选型频段也要选41 2. …

04—常用方法和正则表达式

一、字符串 1.length 属性返回字符串的长度(字符数)。 2.在字符串中查找字符串 indexOf() 字符串使用 indexOf() 来定位字符串中某一个指定的字符首次出现的位置 如果没找到对应的字符函数返回-1 lastIndexOf() 方法在字符串末尾开始查找字符串出现的位置。 3.replace() 方…

WordPress 图片压缩插件:Compress JPEG PNG images 使用方法

插件介绍 Compress JPEG & PNG images是一款非常好用的图片压缩插件:&#xff0c;非常值得大家安装使用&#xff1b;特别是图片类型网站。其实我们很多服务器磁盘空间是不在乎多那么几十 MB 大小的&#xff0c;但是压缩了图片能提升网站速度&#xff0c;节省宽带&#xff…

计算机本科毕业,「就业」还是「读研」?

如果本科不错能找到较好的工作&#xff0c;建议直接工作&#xff0c;否则可以选择读研。 如果你本科毕业于一所顶尖学府&#xff0c;且技术实力雄厚&#xff0c;那么直接就业可能更为明智&#xff1b;对比而言读研可以为你提供更多的时间和机会去提升自己&#xff0c;尤其是在…

算法1: 素数个数统计

统计n以内的素数个数 素数&#xff1a;只能被1和自身整除的自然数&#xff0c;0和1除外&#xff1b; 举例&#xff1a; 输入&#xff1a;100 输出&#xff1a;25 import java.util.*; class Test1{public static void main(String[] args){int a 100; //输入数字//…

智慧矿山视频智能监控与安全监管方案

一、行业背景 随着全球能源需求的日益增长&#xff0c;矿业行业作为国民经济的重要支柱&#xff0c;其发展日益受到广泛关注。然而&#xff0c;传统矿山管理模式的局限性逐渐显现&#xff0c;如生产安全、人员监管、风险预警等方面的问题日益突出。因此&#xff0c;智慧矿山智…

回归预测 | Matlab实现WOA-BP鲸鱼算法优化BP神经网络多变量回归预测

回归预测 | Matlab实现WOA-BP鲸鱼算法优化BP神经网络多变量回归预测 目录 回归预测 | Matlab实现WOA-BP鲸鱼算法优化BP神经网络多变量回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab实现WOA-BP鲸鱼算法优化BP神经网络多变量回归预测&#xff08;完整源码…

文献阅读:Viv:在 web 上多尺度可视化高分辨率多重生物成像数据

文献介绍 「文献题目」 Viv: multiscale visualization of high-resolution multiplexed bioimaging data on the web 「研究团队」 Nils Gehlenborg&#xff08;美国哈佛医学院&#xff09; 「发表时间」 2022-05-11 「发表期刊」 Nature Methods 「影响因子」 47.9 「DOI…

计算机网络—传输层UDP协议:原理、应用

​ &#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;2月のセプテンバー 1:21━━━━━━️&#x1f49f;──────── 5:21 &#x1f504; ◀️ ⏸ ▶️ ☰ &am…