数据结构与算法学习笔记四---双向链表的表示和实现(C语言)

1.前言

        这篇文章主要介绍双向链表的表示和实现。

2.双向链表

        单链表中只有一个指示直接后继的指针域,由此,从某个结点出发只能顺指针向后寻查其他结点。若要寻查结点的直接前驱,则必须从表头指针出发。换句话说,在单链表中,查找直接后继结点的执行时间为 0(1),而查找直接前驱的执行时间为 0(n)。为克服单链表这种单向性的缺点,可利用双向链表(Double Linked List )。
        双向链表是一种链表数据结构,每个节点都有两个指针,一个指向前一个节点,一个指向后一个节点。

1.定义

// 定义
typedef struct DNode {int data;struct DNode *prev; //前驱节点struct DNode *next; //后继节点
} DNode, *DoubleLinkList;

2.初始化

// 初始化
void initDoubleLinkList(DoubleLinkList *list) {*list = NULL;
}

3.插入

// 插入
void insertAtEnd(DoubleLinkList *list, int data) {DNode *newNode = (DNode *)malloc(sizeof(DNode));newNode->data = data;newNode->next = NULL;if (*list == NULL) {newNode->prev = NULL;*list = newNode;} else {DNode *current = *list;while (current->next != NULL) {current = current->next;}current->next = newNode;newNode->prev = current;}
}

4.清空

void clearDoubleLinkList(DoubleLinkList & doubleLinkList){DoubleLinkList p = doubleLinkList;p->next = nullptr;p->prior = nullptr;
}

5.返回链表中第一个与element相同的数据元素的下标

        释放链表指针,调用free函数。

int locationDoubleLinkListElement(DoubleLinkList doubleLink,int element,int * position){DoubleLinkList p = doubleLink;int j = 1;while (p != NULL) {if (p->data == element) {* position = j;return 1;}p = p->next;j++;}return 0;
}

6.表长

int doubleLinkListLength(DoubleLinkList doubleLinkList){DNode * p = doubleLinkList->next;int length = 1;while (p != NULL) {p = p->next;length ++;}return length;
}

7.前驱节点

int priorDoubleElement(DoubleLinkList doubleLink,int element,int * priorElement){DoubleLinkList p = doubleLink;int j = 1;while (p != NULL) {if (p->data == element) {if (p->prev == NULL) {return 0;}* priorElement = p->prev->data;return 1;}p = p->next;j++;}return 0;
}

8.获取双向链表中的元素

int getDoubleLinkElement(DoubleLinkList doubleLink,int position,int *element){DoubleLinkList current = doubleLink;int j = 1;while (current != NULL && j< position) {current = current->next;j++;}if (!current || j > position) {//第i个元素不存在return 0;}* element = current->data;return 1;
}

9.删除

       和单链表算法相同。

bool deleteDoubleLinkList(DoubleLinkList &doubleLinkList, int position){if (position < 1 || position > doubleLinkListLength(doubleLinkList)) {//删除位置非法return false;}DoubleLinkList p = doubleLinkList;int j = 0;while (p->next != nullptr && j < position - 1) {// 找到要删除结点的前一个结点p = p->next;++j;}if (j < position - 1 || p->next == nullptr) {return false; // 删除位置超出范围}DoubleLinkList q = p->next; // 要删除的结点p->next = q->next; // 前一个结点指向后一个结点free(q); // 释放删除结点的内存return true;
}

10.后继节点

int nextDoubleElement(DoubleLinkList doubleLink, int element, int *nextElement){DoubleLinkList p = doubleLink->next; // 从第一个数据节点开始遍历while (p != NULL) {if (p->data == element) {if (p->next == NULL) {return 0; // 没有后继节点}*nextElement = p->next->data; // 将后继节点的数据赋值给nextElementreturn 1; // 找到后继节点,返回1}p = p->next; // 移动到下一个节点}return 0; // 没有找到指定元素
}

11.遍历节点

void traverseDoubleLinkList(DoubleLinkList & doubleLinkList){DoubleLinkList p = doubleLinkList->next;while (p != nullptr) {cout<<p->data<<"\t";p = p->next;}cout<<endl;
}

12.完整代码

#include <stdio.h>
#include <stdlib.h>// 定义
typedef struct DNode {int data;struct DNode *prev; //前驱节点struct DNode *next; //后继节点
} DNode, *DoubleLinkList;// 初始化
void initDoubleLinkList(DoubleLinkList *list) {*list = NULL;
}
// 双向链表是否为空
int doubleLinkListEmpty(DoubleLinkList doubleLinkList){return doubleLinkList == NULL;
}
// 获取第position位置的数据元素
int getDoubleLinkElement(DoubleLinkList doubleLink,int position,int *element){DoubleLinkList current = doubleLink;int j = 1;while (current != NULL && j< position) {current = current->next;j++;}if (!current || j > position) {//第i个元素不存在return 0;}* element = current->data;return 1;
}
// 返回链表中第一个与element相同的数据元素的下标
int locationDoubleLinkListElement(DoubleLinkList doubleLink,int element,int * position){DoubleLinkList p = doubleLink;int j = 1;while (p != NULL) {if (p->data == element) {* position = j;return 1;}p = p->next;j++;}return 0;
}
// 前驱节点
int priorDoubleElement(DoubleLinkList doubleLink,int element,int * priorElement){DoubleLinkList p = doubleLink;int j = 1;while (p != NULL) {if (p->data == element) {if (p->prev == NULL) {return 0;}* priorElement = p->prev->data;return 1;}p = p->next;j++;}return 0;
}
// 后继节点
int nextDoubleElement(DoubleLinkList doubleLink, int element, int *nextElement){DoubleLinkList p = doubleLink->next; // 从第一个数据节点开始遍历while (p != NULL) {if (p->data == element) {if (p->next == NULL) {return 0; // 没有后继节点}*nextElement = p->next->data; // 将后继节点的数据赋值给nextElementreturn 1; // 找到后继节点,返回1}p = p->next; // 移动到下一个节点}return 0; // 没有找到指定元素
}
// 删除双向链表中的指定位置的节点
int deleteDoubleLinkList(DoubleLinkList *doubleLink, int index) {if (*doubleLink == NULL || index < 1) {// 链表为空或者索引无效,无法删除return 0;}DoubleLinkList current = *doubleLink;// 如果要删除的是第一个节点if (index == 1) {*doubleLink = current->next; // 更新头指针if (*doubleLink != NULL) {(*doubleLink)->prev = NULL; // 更新新的第一个节点的前驱指针}free(current); // 释放删除的节点内存return 1; // 返回删除成功}int position = 1;// 查找要删除的节点while (current != NULL && position < index) {current = current->next;position++;}// 如果current为空,说明索引超出范围if (current == NULL) {return 0; // 删除失败}// 处理前驱节点的next指针if (current->prev != NULL) {current->prev->next = current->next;}// 处理后继节点的prev指针if (current->next != NULL) {current->next->prev = current->prev;}// 释放要删除的节点的内存free(current);return 1; // 返回删除成功
}// 插入
void insertAtEnd(DoubleLinkList *list, int data) {DNode *newNode = (DNode *)malloc(sizeof(DNode));newNode->data = data;newNode->next = NULL;if (*list == NULL) {newNode->prev = NULL;*list = newNode;} else {DNode *current = *list;while (current->next != NULL) {current = current->next;}current->next = newNode;newNode->prev = current;}
}// 使用数组生成测试数据
void createDoubleLinkList(DoubleLinkList *linkList, int values[], int size) {initDoubleLinkList(linkList); // Initialize the doubly linked listfor (int i = 0; i < size; i++) {insertAtEnd(linkList, values[i]); // Insert each element into the list}
}
int doubleLinkListLength(DoubleLinkList doubleLinkList){DNode * p = doubleLinkList->next;int length = 1;while (p != NULL) {p = p->next;length ++;}return length;
}
// 打印
void printList(DoubleLinkList list) {printf("**********\t链表数据元素\t**********\n");while (list != NULL) {printf("%d ", list->data);list = list->next;}printf("\n");
}
void traverseDoubleLinkList(DoubleLinkList doubleLinkList){while (doubleLinkList != NULL) {printf("%d ", doubleLinkList->data);doubleLinkList = doubleLinkList->next;}printf("\n");
}
void debugDoubleLinkInformation(char * string){printf("\n********** [测试%s] **********\n",string);
}
int main(int argc, const char *argv[]) {DoubleLinkList list;int element;printf("初始化双向节点\t✅\n");initDoubleLinkList(&list);if (doubleLinkListEmpty(list)) {printf("空链表\t✅\n");}// 使用数据初始化数据int testData[] = {10, 20, 30, 40, 50};int dataSize = sizeof(testData) / sizeof(testData[0]);// 创建双向链表测试数据createDoubleLinkList(&list, testData, dataSize);// 打印双向链表中的元素printList(list);debugDoubleLinkInformation("双向链表表长");if (!doubleLinkListEmpty(list)) {printf("链表不为空,表长:%d\t✅\n",doubleLinkListLength(list));}debugDoubleLinkInformation("双向链表中的数据元素");if (!getDoubleLinkElement(list, 0, &element)) {printf("非法位置\t✅\n");}if (getDoubleLinkElement(list, 1, &element)) {printf("第1个位置的数据元素下标为:%d\t✅\n",element);}if (getDoubleLinkElement(list, 2, &element)) {printf("第2个位置的数据元素下标为:%d\t✅\n",element);}if (getDoubleLinkElement(list, 5, &element)) {printf("第5个位置的数据元素下标为:%d\t✅\n",element);}if (!getDoubleLinkElement(list, 6, &element)) {printf("非法位置\t✅\n");}debugDoubleLinkInformation("双向链表中的下标");int position;if (locationDoubleLinkListElement(list, 10, &position)) {printf("数据元素10的下标为:%d\t✅\n",position);}if (locationDoubleLinkListElement(list, 20, &position)) {printf("数据元素20的下标为:%d\t✅\n",position);}if (locationDoubleLinkListElement(list, 50, &position)) {printf("数据元素50的下标为:%d\t✅\n",position);}if (!locationDoubleLinkListElement(list, 60, &position)) {printf("数据元素50的下标不存在\t✅\n");}debugDoubleLinkInformation("双向链表中的前驱节点");int priorElement;if (!priorDoubleElement(list, 10, &priorElement)) {printf("数据元素10的前驱节点不存在\t✅\n");}if (priorDoubleElement(list, 20, &priorElement)) {printf("数据元素20的前驱节点为:%d\t✅\n",priorElement);}if (priorDoubleElement(list, 30, &priorElement)) {printf("数据元素30的前驱节点为:%d\t✅\n",priorElement);}if (priorDoubleElement(list, 40, &priorElement)) {printf("数据元素40的前驱节点为:%d\t✅\n",priorElement);}if (priorDoubleElement(list, 50, &priorElement)) {printf("数据元素40的前驱节点为:%d\t✅\n",priorElement);}if (!priorDoubleElement(list, 100, &priorElement)) {printf("数据元素100的前驱节点不存在\t✅\n");}debugDoubleLinkInformation("双向链表中的后继节点");int nextElement;if (nextDoubleElement(list, 10, &nextElement)) {printf("数据元素10的后继节点为:%d\t✅\n",nextElement);}if (nextDoubleElement(list, 20, &nextElement)) {printf("数据元素20的后继节点为:%d\t✅\n",nextElement);}if (nextDoubleElement(list, 30, &nextElement)) {printf("数据元素30的后继节点为:%d\t✅\n",nextElement);}if (nextDoubleElement(list, 40, &nextElement)) {printf("数据元素40的后继节点为:%d\t✅\n",nextElement);}if (!nextDoubleElement(list, 50, &nextElement)) {printf("数据元素50的后继节点不存在\t✅\n");}if (!nextDoubleElement(list, 100, &nextElement)) {printf("数据元素100的后继节点不存在\t✅\n");}debugDoubleLinkInformation("双向链表删除功能");printf("删除之前的链表\n");traverseDoubleLinkList(list);if (deleteDoubleLinkList(&list, 5)) {printf("删除之后的链表\n");traverseDoubleLinkList(list);}else{printf("删除失败\n");}return 0;
}

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

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

相关文章

​​​【收录 Hello 算法】第 6 章 哈希表

目录 第 6 章 哈希表 本章内容 第 6 章 哈希表 Abstract 在计算机世界中&#xff0c;哈希表如同一位聪慧的图书管理员。 他知道如何计算索书号&#xff0c;从而可以快速找到目标图书。 本章内容 6.1 哈希表6.2 哈希冲突6.3 哈希算法6.4 小结

日志:打印技巧

一、概览 Unity日志打印技巧 常规日志打印彩色日志日志存储与上传日志开关日志双击溯源 二、常规日志打印 1、打印Hello World 调用堆栈可以很好的帮助我们定位问题&#xff0c;特别是报错的Error日志 Debug.Log("Hello World");Debug.Log("This is a log m…

【java】代理

什么是代理 假设有一个核心方法叫转账&#xff0c;为了安全性考虑&#xff0c;不能让用户直接访问接口。此时涉及到了代理&#xff0c;这使得用户只能访问代理类&#xff0c;增加了访问限制。 代理的定义&#xff1a;给目标对象提供一个代理对象&#xff0c;并且由代理对象控…

egg.socket.io后端开发

1. 安装egg-socket.io npm i egg-socket.io4.1.6 --save最好指定egg-socket.io的版本号&#xff0c;比如我后端使用"egg-socket.io": "^4.1.6", 因为跟前端的socket.io-client是一一关联&#xff0c;socket.io-client用高版本或低版本&#xff0c;都不兼容…

果蔬经营平台|基于SSM+vue的果蔬经营平台系统的设计与实现(源码+数据库+文档)

果蔬经营平台系统 目录 基于SSM&#xff0b;vue的果蔬经营平台系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介…

Python面试50题!面试巩固必看!

题目001: 在Python中如何实现单例模式。 点评&#xff1a;单例模式是指让一个类只能创建出唯一的实例&#xff0c;这个题目在面试中出现的频率极高&#xff0c;因为它考察的不仅仅是单例模式&#xff0c;更是对Python语言到底掌握到何种程度&#xff0c;建议大家用装饰器和元类…

【卫星影像三维重建-全流程代码实现】点云Mesh重构

点云—>Mesh模型 1.介绍1.1 背景1.2 效果示意 2 算法实现2.1 依赖库2.2 实验数据2.3 代码实现2.4 实验效果 3.总结 1.介绍 1.1 背景 &#xff08;1&#xff09;本文主要内容是将三维点云&#xff08;离散的三维点&#xff09;进行表面重建生成Mesh网格&#xff0c;之前有篇…

IP跳变是什么,有什么作用?

IP跳变&#xff0c;简单来说&#xff0c;就是用户在使用网络时&#xff0c;不固定使用一个IP地址&#xff0c;而是定期或根据需求切换到另一个IP地址。这种技术为用户在网络环境中提供了一定的灵活性和安全性。 首先&#xff0c;我们来看看IP跳变的具体实现方式。当用户需要切…

UML快速入门篇

目录 1. UML概述 2. 类的表示 2.1. 类的表示 2.2. 抽象类的表示 2.3. 接口的表示 3. 类的属性&#xff0c;方法&#xff0c;访问权限的表示 3.1. 类的属性 3.2. 类的方法 3.3. 类的权限 4. 类的关联 4.1. 单向关联 4.2. 双向关联 4.3. 自关联 4.4. 类的聚合 4.5.…

Lists.partition用法详解

文章目录 一、引入依赖二、用法三、输出 一、引入依赖 依赖于谷歌的guava 包 <!-- guava --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>28.1-jre</version></dependency&…

仿照el-upload 封装自己的上传控件(el-upload 移动端无法吊起相机)

input选择图片的那个选择在h5的时候在去年下半年突然无法无法出现唤醒相机的选项 不知道出现的原因 发现el-upload作为h5的时候无法吊起相机 又因为需要对服务端地址图片进行回显&#xff08;处于编辑功能的情况下 非新增 新增el-upload 可以实现回显&#xff09; 两个功…

如文所示:

影响 ConnectWise 的 ScreenConnect 远程桌面访问产品的严重漏洞已被广泛利用来传播勒索软件和其他类型的恶意软件。 ConnectWise 于 2 月 19 日通知客户&#xff0c;它已发布针对关键身份验证绕过缺陷和高严重性路径遍历问题的补丁。该安全漏洞当时没有 CVE 标识符。第二天&am…

绘唐3启动器怎么启动一键追爆款3正式版

绘唐3启动器怎么启动一键追爆款3正式版 工具入口 一.文案助手&#xff1a; 【注意&#xff01;&#xff01;】如果图片无显示&#xff0c;一般情况下被杀毒拦截&#xff0c;需关闭杀毒软件或者信任文件路径。 win10设置排除文件&#xff1a; 1.【新建工程】使用前先新建工程…

C语言循环队列

以下是一个使用 C 语言实现的简单循环队列示例&#xff1a; #include <stdio.h> #include <stdlib.h>#define MAX_SIZE 5// 定义循环队列结构体 typedef struct {int items[MAX_SIZE];int front, rear; } Queue;// 初始化队列 void initQueue(Queue *q) {q->fr…

维修贝加莱 MAHLO 5PP320.1043-K08 控制器 主板 工控电脑 触摸屏深圳捷达工控维修

贝加莱HMI面板5PP320.1043-39 扩展产品类型&#xff1a;HMI/触摸屏 产品编号&#xff1a;5PP320.1043-39 B&R 类型名称&#xff1a;HMI/触摸屏 描述 电源面板 PP320 BIOS&#xff1b; 10.4英寸VGA彩色TFT显示屏&#xff0c;带触摸屏&#xff08;电阻式&#xff09;&#xf…

谷歌Gboard应用的语言模型创新:提升打字体验的隐私保护技术

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

blender cell fracture制作破碎效果,将一个模型破碎成多个模型

效果&#xff1a; 1.打开编辑-》偏好设置。搜索cell&#xff0c;勾选上如下图所示的&#xff0c;然后点击左下角菜单里的保存设置。 2.选中需要破碎的物体&#xff0c;按快捷键f3&#xff08;快速搜索插件&#xff09;&#xff0c;搜索cell fracture。 3.调整自己需要的参数配置…

广东省网络安全竞赛部分web,misc wp

我的队伍只做了5题&#xff0c;还是太菜了&#xff0c;本来不想发的&#xff0c;但是写都写了&#xff0c;还是水一篇博客吧 这里是我们队的wp misc1 给了一个压缩包&#xff0c;解压需要密码&#xff0c;用纯数字密码没跑出来&#xff0c;感觉可能不是要强跑&#xff0c;看…

[力扣题解] 343. 整数拆分

题目&#xff1a;343. 整数拆分 思路 动态规划 状态转移方程&#xff1a; 3个数取最大 自己&#xff1b;拆成2个数&#xff1b;先拆一个数i - j&#xff0c;剩下的拆分交给前面f[j]&#xff1b; 代码 // 注意&#xff1a;没有给定k的值, 是自己定的 // f[i] : 拆成i个的最…

今天开发了一款软件,我竟然只用敲了一个字母(文末揭晓)

软件课题&#xff1a;Python实现打印100内数学试题软件及开发过程 一、需求管理&#xff1a; 1.实现语言&#xff1a;Python 2.打印纸张&#xff1a;A4 3.铺满整张纸 4.打包成exe 先看效果&#xff1a; 1. 2.电脑打印预览 3.打印到A4纸效果&#xff08;晚上拍的&#x…