数据结构(一)链表

目录

链表

单向链表

单向链表结构与基本操作

插入节点

删除节点

搜索节点

遍历链表

反转链表

双向链表

双向链表结构与基本操作

节点定义和创建

插入节点

删除节点

搜索节点

遍历链表

转链表反


在开始讲线性表之前,先给各位读者重新回顾一下链表

链表

单向链表

单向链表是一种基本的数据结构,由一系列节点组成,每个节点包含两部分:数据域指针域数据域存储数据,而指针域存储指向列表中下一个节点的指针。单向链表的特点是数据元素的排列呈现单一方向,即每个节点仅指向下一个节点,直到最后一个节点的指针通常指向NULL,表示链表的结束。

单向链表结构与基本操作

在C语言中,单向链表的节点可以这样定义

typedef struct Node {int data;                 // 数据域,存储节点的数据struct Node* next;        // 指针域,指向下一个节点的指针
} Node;

 创建节点:初始化一个节点,通常需要提供数据,节点的next指针设置为NULL

Node* createNode(int data) {Node* newNode = (Node*)malloc(sizeof(Node));if (newNode != NULL) {newNode->data = data;newNode->next = NULL;}return newNode;
}
插入节点

插入节点的基本过程:首先找到正确位置p,然后申请新结点t并对t的结点信息赋值,最后讲t插在p之后

插入到头部

void insertAtHead(Node** head, int data) {Node* newNode = createNode(data);newNode->next = *head;*head = newNode;
}

插入到尾部
在链表尾部插入节点需要遍历整个链表,直到找到最后一个节点。

void insertAtTail(Node** head, int data) {Node* newNode = createNode(data);if (*head == NULL) {*head = newNode;} else {Node* temp = *head;while (temp->next != NULL) {temp = temp->next;}temp->next = newNode;}
}

插入到特定位置

在链表的特定位置插入节点需要遍历链表直到达到指定位置的前一个节点。

void insertAtPosition(Node** head, int data, int position) {Node* newNode = createNode(data);if (position == 1) {insertAtHead(head, data);} else {Node* temp = *head;for (int i = 1; temp != NULL && i < position - 1; i++) {temp = temp->next;}if (temp == NULL) {printf("Position exceeds the length of the list.\n");} else {newNode->next = temp->next;temp->next = newNode;}}
}
删除节点

注意:删除一个节点后必须释放该节点的空间

删除头部节点

void deleteFromHead(Node** head) {if (*head != NULL) {Node* temp = *head;*head = (*head)->next;free(temp);}
}

删除尾部节点

删除尾部节点需要遍历整个链表以找到倒数第二个节点。

void deleteFromTail(Node** head) {if (*head == NULL) return;if ((*head)->next == NULL) {free(*head);*head = NULL;} else {Node* temp = *head;while (temp->next->next != NULL) {temp = temp->next;}free(temp->next);temp->next = NULL;}
}

删除特定位置的节点

删除特定位置的节点需要遍历链表直到找到该位置的前一个节点。

void deleteAtPosition(Node** head, int position) {if (*head == NULL) return;if (position == 1) {deleteFromHead(head);} else {Node* temp = *head;for (int i = 1; temp != NULL && i < position - 1; i++) {temp = temp->next;}if (temp == NULL || temp->next == NULL) {printf("Position exceeds the length of the list.\n");} else {Node* next = temp->next->next;free(temp->next);temp->next = next;}}
}
搜索节点

搜索节点涉及遍历链表以查找具有特定值的节点。

Node* search(Node* head, int key) {Node* current = head;while (current != NULL) {if (current->data == key) {return current;}current = current->next;}return NULL;
}
遍历链表

遍历链表是访问链表中每个节点的基本操作。

void printList(Node* head) {Node* current = head;while (current != NULL) {printf("%d -> ", current->data);current = current->next;}printf("NULL\n");
}
反转链表

反转链表需要改变每个节点的指针方向。

Node* reverseList(Node* head) {Node* prev = NULL;Node* current = head;Node* next = NULL;while (current != NULL) {next = current->next;current->next = prev;prev = current;current = next;}return prev;
}

双向链表

双向链表(Doubly Linked List)是一种链式数据结构,其中每个节点包含数据部分以及两个指针,分别指向前一个节点和后一个节点。这种结构允许从两个方向遍历链表:向前和向后。双向链表在插入和删除操作中比单向链表更加灵活,因为可以直接访问前驱和后继节点。

双向链表结构与基本操作

节点定义和创建

创建一个双向链表节点,需要初始化数据和两个指针:

typedef struct Node {int data;                 // 数据域,存储节点的数据struct Node* prev;        // 指向前一个节点的指针struct Node* next;        // 指向下一个节点的指针
} Node;Node* createNode(int data) {Node* newNode = (Node*)malloc(sizeof(Node));if (newNode != NULL) {newNode->data = data;newNode->prev = NULL;newNode->next = NULL;}return newNode;
}
插入节点

插入到头部

在头部插入节点,需要更新头节点的prev指针和新节点的next指针。

void insertAtHead(Node** head, int data) {Node* newNode = createNode(data);newNode->next = *head;if (*head != NULL) {(*head)->prev = newNode;}*head = newNode;
}

插入到尾部

在尾部插入节点,需要遍历链表找到尾节点,然后更新尾节点的next指针和新节点的prev指针。

void insertAtTail(Node** head, int data) {Node* newNode = createNode(data);if (*head == NULL) {*head = newNode;} else {Node* temp = *head;while (temp->next != NULL) {temp = temp->next;}temp->next = newNode;newNode->prev = temp;}
}

插入到特定位置

在特定位置插入节点,需要更新前一个节点的next指针和新节点的prev指针,以及新节点的next指针和后一个节点的prev指针。

void insertAtPosition(Node** head, int data, int position) {Node* newNode = createNode(data);if (position == 1) {insertAtHead(head, data);} else {Node* temp = *head;for (int i = 1; temp != NULL && i < position - 1; i++) {temp = temp->next;}if (temp == NULL) {printf("Position exceeds the length of the list.\n");} else {newNode->next = temp->next;newNode->prev = temp;if (temp->next != NULL) {temp->next->prev = newNode;}temp->next = newNode;}}
}
删除节点

删除头部节点:删除头部节点,需要更新头节点的prev指针和新头节点的next指针。

void deleteFromHead(Node** head) {if (*head == NULL) return;Node* temp = *head;*head = (*head)->next;if (*head != NULL) {(*head)->prev = NULL;}free(temp);
}

删除尾部节点:删除尾部节点,需要遍历链表找到倒数第二个节点,然后更新其next指针

void deleteFromTail(Node** head) {if (*head == NULL) return;if ((*head)->next == NULL) {free(*head);*head = NULL;} else {Node* temp = *head;while (temp->next->next != NULL) {temp = temp->next;}free(temp->next);temp->next = NULL;}
}

删除特定位置的节点:删除特定位置的节点,需要更新前一个节点的next指针和后一个节点的prev指针

void deleteAtPosition(Node** head, int position) {if (*head == NULL) return;if (position == 1) {deleteFromHead(head);} else {Node* temp = *head;for (int i = 1; temp != NULL && i < position; i++) {temp = temp->next;}if (temp == NULL) {printf("Position exceeds the length of the list.\n");} else {if (temp->prev != NULL) {temp->prev->next = temp->next;}if (temp->next != NULL) {temp->next->prev = temp->prev;}free(temp);}}
}
搜索节点

搜索节点,需要遍历链表直到找到具有特定值的节点。

Node* search(Node* head, int key) {Node* current = head;while (current != NULL) {if (current->data == key) {return current;}current = current->next;}return NULL;
}
遍历链表

向前遍历

void printListForward(Node* head) {Node* current = head;while (current != NULL) {printf("%d -> ", current->data);current = current->next;}printf("NULL\n");
}

向后遍历

void printListBackward(Node* head) {Node* current = head;while (current != NULL) {printf("%d <- ", current->data);current = current->prev;}printf("NULL\n");
}
转链表反
void reverseList(Node** head) {Node* temp = NULL;Node* current = *head;while (current != NULL) {temp = current->prev;current->prev = current->next;current->next = temp;current = current->prev;}if (temp != NULL) {*head = temp->prev;}
}

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

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

相关文章

linux从0到1——shell编程7

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…

微软发布Win11 24H2系统11月可选更新KB5046740!

系统之家11月22日报道&#xff0c;微软针对Win11 24H2系统推出2024年11月最新可选更新补丁KB5046740&#xff0c;更新后系统版本后升至26100.2454&#xff0c;此次更新后修复当应用程序以PDF和XLSX格式导出图表对象时停止响应、无法使用API查找旋转信息等问题。以下小编将给大家…

五天SpringCloud计划——DAY2之使用Docker完成项目的部署

一、引言 刚刚学完了Docker的使用&#xff0c;现在知识在脑子里面还是热乎的&#xff0c;是时候把它总结一下了。 现在的我认为Docker时一个部署项目的工具(不知道是不是真的),相比于我以前使用宝塔面板部署项目&#xff0c;使用Docker更能让我看到代码之美&#xff0c;怎么一…

设计模式之 模板方法模式

模板方法模式是行为型设计模式的一种。它定义了一个算法的骨架&#xff0c;并将某些步骤的实现延迟到子类中。模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些特定步骤。 模板方法模式的核心在于&#xff1a; 封装算法的骨架&#xff1a;通过父类中的模板方…

Softing线上研讨会 | Ethernet-APL:推动数字时代的过程自动化

| &#xff08;免费&#xff09;线上研讨会时间&#xff1a;2024年11月19日 16:00~16:30 / 23:00~23:30 Ethernet-APL以10Mb/s的传输速率为过程工业中的现场设备带来了无缝以太网连接和本质安全电源&#xff0c;这不仅革新了新建工厂&#xff0c;也适用于改造现有工厂。 与现…

Idea修改Commit Changes模式、idea使用git缺少部分Commit Changes

文章目录 一、模式一1、页面效果如下2、如何打开为这种样式&#xff1f; 二、模式二1、页面效果如下2、如何打开为这种样式&#xff1f; 三、总结 前言&#xff1a;Idea中代码提交到git库时的commit Change有两种模式&#xff0c;每种模式的界面及功能都不太一样。 Commit Cha…

东土科技孵化的“网联汽车高速通信技术”前沿产品亮相2024WICV大会

2024世界智能网联汽车大会&#xff08;WICV&#xff09;于近日在北京召开。本次大会发布了由中国汽车工程学会组织全球200余位专家&#xff0c;联合评审遴选出未来十年对于智能网联汽车发展具有重要影响的十大技术趋势&#xff0c;包括“面向高级别自动驾驶的超级人工智能”“网…

使用itextpdf进行pdf模版填充中文文本时部分字不显示问题

在网上找了很多种办法 都解决不了; 最后发现是文本域字体设置出了问题; 在这不展示其他的代码 只展示重要代码; 1 引入扩展包 <dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</v…

HTML5实现剪刀石头布小游戏(附源码)

文章目录 1.设计来源1.1 主界面1.2 皮肤风格1.2 游戏中界面 2.效果和源码源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/143798520 HTM…

自动驾驶之激光雷达

这里写目录标题 1 什么是激光雷达2 激光雷达的关键参数3 激光雷达种类4 自动驾驶感知传感器5 激光雷达感知框架5.1 pointcloud_preprocess5.2 pointcloud_map_based_roi5.3 pointcloud_ground_detection5.4 lidar_detection5.5 lidar_detection_filter5.6 lidar_tracking 1 什么…

pycharm在使用conda虚拟环境时Terminal爆红问题

问题&#xff1a; 解决方法&#xff1a; 复制cmd.exe后面所有路径 添加到pycharm的shell path中&#xff1a;

《Python浪漫的烟花表白特效》

一、背景介绍 烟花象征着浪漫与激情&#xff0c;将它与表白结合在一起&#xff0c;会创造出别具一格的惊喜效果。使用Python的turtle模块&#xff0c;我们可以轻松绘制出动态的烟花特效&#xff0c;再配合文字表白&#xff0c;打造一段专属的浪漫体验。 接下来&#xff0c;让…

二次封装的天气时间日历选择组件

这个接口没调通 没有数据展示~ userStore.badgeDate是VUEX全部存的日历数据 <template><!-- 日历组件 --><el-date-pickerref"elPicker":size"size"v-model"dateTimeValue":type"dateType":range-separator"rang…

当你项目服务器磁盘报警

当你们公司运维收到这样的邮件&#xff0c;大概率日志文件过大引起的 在Linux下如何不停止服务,清空nohup.out文件呢&#xff1f; nohup.out会一直一直自己增长下去&#xff0c;如果你的服务器硬盘不给力的话&#xff0c;很容易把应用也挂掉&#xff08;硬盘没空间 &#xff0…

低速接口项目之串口Uart开发(四)——UART串口实现FPGA内部AXILITE寄存器的读写控制

本节目录 一、设计背景 二、设计思路 三、逻辑设计框架 四、仿真验证 五、上板验证 六、往期文章链接本节内容 一、设计背景 通常&#xff0c;芯片手册或者IP都会提供一系列的用户寄存器以及相关的定义&#xff0c;用于软件开发人员进行控制底层硬件来调试&#xff0c;或封装…

git branch -d 删除分支

Git进行版本控制时&#xff0c;删除分支是常见的操作。特别是当特定的功能开发完成或者分支不再需要时&#xff0c;删除分支可以帮助保持仓库的整洁。删除本地分支和删除远端分支是两个独立的操作。如果需要同时删除本地和远端的分支&#xff0c;需要分别执行以下两个命令。 一…

Linux环境开启MongoDB的安全认证

文章目录 1. MongoDB安全认证简介1.1 访问控制1.2 角色1.3 权限 2. MongoDB中的常见角色3. MongoDB Shell3.1 下载MongoDB Shell3.2 通过MongoDB Shell连接MongoDB 4. 创建管理员用户5. 为具体的数据库创建用户6. 开启权限认证7. 重启MongoDB服务8. 连接MongoDB9. MongoDB数据库…

PVE的优化与温度监控(二)—无法识别移动硬盘S.M.A.R.T信息的思考并解决

前情提要&#xff1a;空闲2.5英寸机械硬盘&#xff0c;直接放到PVE上测试NAS 使用&#xff0c;通过SATA线的方式让小主机不太美观&#xff0c;并且失去了前期调试的安全性。购入移动硬盘盒&#xff0c;缺点&#xff0c;USB 连接&#xff0c;会失去一些特性。比如本文中遇到的问…

嵌入式硬件实战基础篇(二)-稳定输出3.3V的太阳能电池-无限充放电

引言&#xff1a;本内容主要用作于学习巩固嵌入式硬件内容知识&#xff0c;用于想提升下述能力&#xff0c;针对学习稳压芯片和电容以及电池之间的运用&#xff0c;对于硬件PCB以及原理图的练习和前面硬件篇的实际运用&#xff1b;太阳能是一种清洁、可再生的能源&#xff0c;广…

OpenCV相机标定与3D重建(3)校正鱼眼镜头畸变的函数calibrate()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::fisheye::calibrate 函数是 OpenCV 中用于校正鱼眼镜头畸变的一个重要函数。该函数通过一系列棋盘格标定板的图像来计算相机的内参矩阵和畸变…