趣味数据结构之——链

记得数组吗,一个萝卜一个坑的想象。在数组的世界里我们就是第三视角,置身于坑外的。如果我们是二维平面上的生物,那数组就是一维的线,我们可以随机访问,增删查改,也可以一眼看出数组大小。

那么对于链来说,我们则是一维链上的一维生物,所能知道的所有信息(即我们能看到的)就只有链定义的信息(比如指向自己当前位置的指针,指向下一个或上一个节点的指针)(这里面的看到,意指我们所掌握的指针)

    //这是双链表template <typename E>class Node {public:E val;Node* next;Node* prev;Node(Node* prev, E element, Node* next) {//这个是构造函数,可以没有this->val = element;this->next = next;this->prev = prev;}};

显然按照我们的设想,我们永远都局限在链的一个节点上,永远都不可能一次性看到整个链,就像我们在地球上一样。于是无法直接计算出链的长度,以及无法直接存取,存储空间并不连续。

增删→

就很高效了,因为(对于有效链)我们一次能看两个(单向链表)或三个结点,那就说明我们可以操作链点的链接,这也是链的核心关键。

我们可以销毁砍掉的结点,也可以链上新定义的节点。

而操作的唯一要点就是处理好结点之间的链接。(就只有两条讯息,prev 前驱指针和next 后继指针)

对于单链表,朝且只能朝着着指针指示下一个结点方向走,依次处理好每个结点存储的指针就好了。(为什么朝且只能朝着呢,因为我们手里的信息就只有那个方向的邻接结点嘛)

对于双链表,只按一个方向走一遍是不够的,因为至少结点n本身的指针会被存储在两个地方——n+1,和n-1。你需要带着n的指针前往n-1处和n+1处将它存下。于是想要处理好一个结点就需要处理好这个结点两端的链接处。记住,链接处只和与之相连的两个结点有关系。(这时候就可以不止往两个方向走了,因为我们在结点处手头可是握有两个方向的讯息)

定义单链表→

本结点的存储,后继结点的讯息(指针)

class ListNode {
public:int val;ListNode *next;ListNode(int x) : val(x), next(NULL) {}
};// 输入一个数组,转换为一条单链表
ListNode* createLinkedList(std::vector<int> arr) {if (arr.empty()) {return nullptr;}ListNode* head = new ListNode(arr[0]);ListNode* cur = head;for (int i = 1; i < arr.size(); i++) {cur->next = new ListNode(arr[i]);cur = cur->next;}return head;
}

查/改→

// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});// 遍历单链表
for (ListNode* p = head; p != nullptr; p = p->next) {std::cout << p->val << std::endl;
}

增→

需要处理被增结点n,那就需要处理它两端的链接处,需要处理好链接处,那就只和链接处接壤的结点有关,所以这里只要处理好三个结点里的信息存储(即指针)就好了。

插入头部→

// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});// 在单链表头部插入一个新节点 0
ListNode* newNode = new ListNode(0);
newNode->next = head;
head = newNode;// 现在链表变成了 0 -> 1 -> 2 -> 3 -> 4 -> 5

插入尾部→

// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});// 在单链表尾部插入一个新节点 6
ListNode* p = head;
// 先走到链表的最后一个节点
while (p->next != nullptr) {p = p->next;
}
// 现在 p 就是链表的最后一个节点
// 在 p 后面插入新节点
p->next = new ListNode(6);// 现在链表变成了 1 -> 2 -> 3 -> 4 -> 5 -> 6

插入中间→

// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});// 在第 3 个节点后面插入一个新节点 66
// 先要找到前驱节点,即第 3 个节点
ListNode* p = head;
for (int i = 0; i < 2; i++) {p = p->next;
}
// 此时 p 指向第 3 个节点
// 组装新节点的后驱指针
ListNode* newNode = new ListNode(66);
newNode->next = p->next;// 插入新节点
p->next = newNode;// 现在链表变成了 1 -> 2 -> 3 -> 66 -> 4 -> 5

删中间→

// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});// 删除第 4 个节点,要操作前驱节点
ListNode* p = head;
for (int i = 0; i < 2; i++) {p = p->next;
}// 此时 p 指向第 3 个节点,即要删除节点的前驱节点
// 把第 4 个节点从链表中摘除
p->next = p->next->next;// 现在链表变成了 1 -> 2 -> 3 -> 5

删尾部→

// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});// 删除尾节点
ListNode* p = head;
// 找到倒数第二个节点
while (p->next->next != nullptr) {p = p->next;
}// 此时 p 指向倒数第二个节点
// 把尾节点从链表中摘除
p->next = nullptr;// 现在链表变成了 1 -> 2 -> 3 -> 4

删头部→

// 创建一条单链表
ListNode* head = createLinkedList(vector<int>{1, 2, 3, 4, 5});// 删除头结点
head = head->next;// 现在链表变成了 2 -> 3 -> 4 -> 5

同理对于双链表也就很简单了,处理好各个结点存储的讯息就完事大吉了

定义

class DoublyListNode {
public:int val;DoublyListNode *next, *prev;DoublyListNode(int x) : val(x), next(NULL), prev(NULL) {}
};DoublyListNode* createDoublyLinkedList(vector<int>& arr) {if (arr.empty()) {return NULL;}DoublyListNode* head = new DoublyListNode(arr[0]);DoublyListNode* cur = head;// for 循环迭代创建双链表for (int i = 1; i < arr.size(); i++) {DoublyListNode* newNode = new DoublyListNode(arr[i]);cur->next = newNode;newNode->prev = cur;cur = cur->next;}return head;
}

遍历/查找/修改

// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});
DoublyListNode* tail = nullptr;// 从头节点向后遍历双链表
for (DoublyListNode* p = head; p != nullptr; p = p->next) {cout << p->val << endl;tail = p;
}// 从尾节点向前遍历双链表
for (DoublyListNode* p = tail; p != nullptr; p = p->prev) {cout << p->val << endl;
}

头部插入→

// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});// 在双链表头部插入新节点 0
DoublyListNode* newHead = new DoublyListNode(0);
newHead->next = head;
head->prev = newHead;
head = newHead;// 现在链表变成了 0 -> 1 -> 2 -> 3 -> 4 -> 5

尾部插入→

// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});DoublyListNode* tail = head;
// 先走到链表的最后一个节点
while (tail->next != nullptr) {tail = tail->next;
}// 在双链表尾部插入新节点 6
DoublyListNode* newNode = new DoublyListNode(6);
tail->next = newNode;
newNode->prev = tail;
// 更新尾节点引用
tail = newNode;// 现在链表变成了 1 -> 2 -> 3 -> 4 -> 5 -> 6

中间插入→

// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});// 想要插入到索引 3(第 4 个节点)
// 需要操作索引 2(第 3 个节点)的指针
DoublyListNode* p = head;
for (int i = 0; i < 2; i++) {p = p->next;
}// 组装新节点
DoublyListNode* newNode = new DoublyListNode(66);
newNode->next = p->next;
newNode->prev = p;// 插入新节点
p->next->prev = newNode;
p->next = newNode;// 现在链表变成了 1 -> 2 -> 3 -> 66 -> 4 -> 5

删中间→

// 创建一个双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});// 删除第 4 个节点
// 先找到第 3 个节点
DoublyListNode* p = head;
for (int i = 0; i < 2; ++i) {p = p->next;
}// 现在 p 指向第 3 个节点,我们将它后面那个节点摘除出去
DoublyListNode* toDelete = p->next;// 把 toDelete 从链表中摘除
p->next = toDelete->next;
toDelete->next->prev = p;// 把 toDelete 的前后指针都置为 null 是个好习惯(可选)
toDelete->next = nullptr;
toDelete->prev = nullptr;// 现在链表变成了 1 -> 2 -> 3 -> 5

删头部→

// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});// 删除头结点
DoublyListNode* toDelete = head;
head = head->next;
head->prev = nullptr;// 清理已删除节点的指针
toDelete->next = nullptr;// 现在链表变成了 2 -> 3 -> 4 -> 5

删尾部→

// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});// 删除头结点
DoublyListNode* toDelete = head;
head = head->next;
head->prev = nullptr;// 清理已删除节点的指针
toDelete->next = nullptr;// 现在链表变成了 2 -> 3 -> 4 -> 5

总结,在链表的定义和操作中,我们要且仅要关注的要点,我们手头握有的讯息(指针(方向)),处理好链接处(也就是处理好每个结点中存储的讯息,这事关链表的结构)。

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

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

相关文章

构建低代码平台的技术解析

低代码平台表单引擎与业务事件设计实践 低代码平台表单引擎与业务事件设计实践一、什么是低代码&#xff1f;它能做什么&#xff1f;二、请假系统案例介绍2.1 主要功能2.2 业务流程 三、表单元数据、实例数据与业务事件联动设计3.1 表单元数据&#xff08;Meta&#xff09;如何…

Hive SQL 快速入门指南

在大数据蓬勃发展的当下&#xff0c;处理海量数据成为企业面临的关键挑战。Hive SQL 作为一款强大的工具&#xff0c;为我们打开了高效处理大数据的大门。接下来&#xff0c;让我们一起踏上 Hive SQL 的入门之旅。​ 一、Hive SQL 是什么​ Hive 是基于 Hadoop 的数据仓库工具…

国内公司把数据湖做成了数据库

在做多年的数据仓库项目&#xff0c;数据湖也在做&#xff0c;但是做完发现&#xff0c;这个不是传统数据库里面的ODS吗&#xff1f; 好多公司做数据湖&#xff0c;就是把数据湖做成了ODS层&#xff08;贴源数据层&#xff09;&#xff0c;难道真的数据湖就是这样等于ODS吗&am…

Python 数据分析与可视化 Day 6 - 可视化整合报告实战

&#x1f3af; 今日目标 整合数据分析与可视化结果生成结构化报告用代码自动生成完整的图文分析文档熟悉 Jupyter Notebook / Markdown 图表 报告生成流程 &#x1f9e9; 一、项目背景&#xff1a;学生成绩分析报告 数据来源&#xff1a;students_cleaned.csv&#xff08;含姓…

服务器、树莓派/香橙派部署HomeAssistant与小爱音箱联动

HomeAssistant功能介绍与多平台部署实战&#xff1a;CentOS服务器、树莓派、香橙派部署及小爱音箱联动控制 一、HomeAssistant简介 HomeAssistant是一款基于Python开发的开源智能家居自动化平台&#xff0c;它最大的特点是高度集成和自定义。通过HomeAssistant&#xff0c;用…

内存泄漏系列专题分析之二十四:内存泄漏测试Camera相机进程内存指标分布report概述

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 内存泄漏系列专题分析之二十四:内存泄漏测试Camera相机进程内存指标分布report概述 目录 一、问题背景 二、:内存泄漏测试Camera相机进程内存指标分布report概述 2.1:Camera领域相机进…

华为堆叠理论及配置

一&#xff0c;堆叠基本概念 1.1交换机角色 主交换机&#xff08;Master&#xff09;&#xff1a;主交换机负责管理整个堆叠。**堆叠系统中只有一台主交换机。**备交换机&#xff08;Standby&#xff09;&#xff1a;备交换机是主交换机的备份交换机。堆叠系统中只有一台备交换…

【数字经济】数据即产品架构在数字经济时代的应用

数据即产品架构在数字经济时代的应用 在数字经济中&#xff0c;数据已成为核心生产要素&#xff0c;“数据即产品”&#xff08;Data-as-a-Product&#xff09;架构通过系统化封装原始数据&#xff0c;实现其可交易、可交付的产品化价值。以下是其架构设计与应用解析&#xff…

MySQL 中的时间序列数据分析与处理

在互联网应用和企业业务系统中&#xff0c;特别是现在当下环境电商以及跨境电商火爆的情况下&#xff0c;时间序列数据无处不在&#xff0c;如电商订单时间、用户登录日志、设备监控数据等。MySQL 作为主流数据库&#xff0c;具备强大的时间序列数据处理能力。本文将结合电商订…

STM32——MDK5编译和串口下载程序+启动模式

一、MDK5编译 1.1 编译中间文件 还可通过 .map文件计算程序大小 中间文件 > 下载到开发板中的文件 > .hex 二、串口下载 2.1 前提须知 2.2 串口硬件链接&#xff08;M3、M4系列&#xff09; M7无串口下载 PC端需安装 CH340 USB 虚拟串口驱动&#xff1a;CH340 USB 虚…

HyperWorks仿真案例:拓扑优化与激光增材制造的完美结合挖掘轻量化结构的新潜力

许多技术创新都基于自然界中生物结构的设计。通过不断进化&#xff0c;大自然在数百万年间已学会根据各种形状的功能对形状进行调整&#xff0c;从而最大程度地提高效率。当工程师设法构建坚固而轻盈的结构时&#xff0c;这些自然界中的示例可以提供重要线索。在目前的研究项目…

在Windows系统部署本地智能问答系统:基于百度云API完整教程

引言 在人工智能时代&#xff0c;搭建私有化智能问答系统能有效保护数据隐私并提升响应效率。本教程将手把手教你在Windows环境中&#xff0c;通过百度云API构建专属智能问答系统&#xff0c;全程无需服务器&#xff0c;仅需本地计算机即可运行&#xff01; 一、环境准备 系统…

Vue的watch函数实现

<script setup> import { watch, ref, reactive, toRefs } from vue;const count ref(0); const obj reactive({name: 张三,age: 18 });// 我们可以使用toRefs&#xff0c;将reactive对象中的属性转换为ref对象&#xff0c;保持响应性&#xff01;&#xff01; const {…

Tomcat 安装使用教程

&#x1f4cc; 什么是 Tomcat&#xff1f; Apache Tomcat 是一个开源的 Java Servlet 容器&#xff0c;也是运行 Java Web 应用最常用的服务器之一&#xff0c;支持 Servlet、JSP 等规范。 &#x1f9f0; 一、准备工作 1. 系统要求 操作系统&#xff1a;Windows / Linux / m…

【邀请】点击邀请链接参加阿里云训练营活动,完成学习送礼品+户外折叠凳,一个小时就能完成

点击邀请链接参加阿里云训练营活动&#xff0c;完成学习送礼品户外折叠凳&#xff0c;快的话一个小时就能完成。 7月28日23:59前完成。 OSS进阶应用与成本优化训练营 礼品如下&#xff1a; 包尖钢笔/祈福小神仙积木/雨伞/不锈钢餐具随机发放 户外折叠凳

用户行为序列建模(篇六)-【阿里】DSIN

简介 DSIN&#xff08;Deep Session Interest Network&#xff09;是阿里巴巴于2019年提出的点击率预估模型。相比于DIN、DIEN&#xff0c;考虑了用户行为序列的内在结构&#xff08;序列是由session组成的&#xff0c;在每个session内&#xff0c;用户行为是高度同构的&#…

现代Web表情选择器组件:分类系统与实现详解

你好呀&#xff0c;我是小邹。今天给博客的emoji表情进行了归类、补充&#xff0c;具体优化如下。 表情选择器的核心价值在于其分类系统。本文将深入解析表情分类体系的设计与实现&#xff0c;通过完整代码示例展示如何构建一个专业级的表情选择器组件。 一、表情分类系统设计…

华为云Flexus+DeepSeek征文 |华为云ModelArts Studio集成OpenAI Translator:开启桌面级AI翻译新时代

华为云FlexusDeepSeek征文 |华为云ModelArts Studio集成OpenAI Translator&#xff1a;开启桌面级AI翻译新时代 引言一、ModelArts Studio平台介绍华为云ModelArts Studio简介ModelArts Studio主要特点 二、OpenAI Translator介绍openai-translator简介openai-translator主要特…

GitHub 趋势日报 (2025年06月27日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 817 twenty 655 awesome 476 free-for-dev 440 Best-websites-a-programmer-shoul…

Java语法通关秘籍:this、构造方法到String核心精粹

文章目录 &#x1f50d; **一、就近原则与this关键字**1. **成员变量**2. **局部变量** &#x1f6e0;️ **二、构造方法&#xff08;构造器&#xff09;**1. **标准格式**2. **有参构造实战**3. **灵魂三问** ❓ &#x1f4e6; **三、JavaBean黄金标准**&#x1f9e0; **四、对…