C++链表详解:从基础概念到高级应用

C++链表详解:从基础概念到高级应用

链表是计算机科学中最基础也是最重要的数据结构之一,它在内存管理、算法实现和实际应用中扮演着关键角色。本文将详细介绍链表的概念、类型、C++实现以及实际应用场景,帮助读者全面理解这一重要的数据结构。

在这里插入图片描述

文章目录

  • C++链表详解:从基础概念到高级应用
    • 1. 链表的基本概念
      • 链表的核心特点
    • 2. 链表的类型
      • 2.1 单向链表
      • 2.2 双向链表
      • 2.3 循环链表
      • 2.4 双向循环链表
    • 3. C++中的链表实现
      • 3.1 节点结构定义
      • 3.2 单向链表实现
      • 3.3 双向链表实现
      • 3.4 循环链表实现
    • 4. 链表的基本操作
      • 4.1 插入操作
      • 4.2 删除操作
      • 4.3 遍历操作
      • 4.4 搜索操作
    • 5. 链表的高级操作
      • 5.1 链表反转
      • 5.2 环检测
      • 5.3 查找中间节点
      • 5.4 合并有序链表
    • 6. 链表的实际应用
      • 6.1 音乐播放列表
      • 6.2 约瑟夫环问题
      • 6.3 多项式表示
      • 6.4 LRU缓存实现
    • 7. 链表与其他数据结构的比较
    • 8. 总结与进阶学习建议
    • 参考资料

1. 链表的基本概念

链表是一种线性数据结构,它由一系列节点组成,每个节点包含两个部分:数据部分和指针部分。与数组不同,链表中的元素在内存中不是连续存储的,而是通过指针连接在一起。这种结构允许在不移动其他元素的情况下进行高效的插入和删除操作。

在这里插入图片描述

如上图所示,链表由一系列节点组成,每个节点包含数据和指向下一个节点的指针。链表的第一个节点称为头节点(HEAD),最后一个节点的指针指向NULL,表示链表的结束。

链表的核心特点

  1. 动态数据结构:链表可以根据需要动态增长和缩小,不需要预先分配固定大小的内存。

  2. 非连续内存分配:链表中的节点可以存储在内存的任何位置,通过指针相互连接。

  3. 插入和删除效率高:在已知位置插入或删除节点的时间复杂度为O(1),而不需要像数组那样移动其他元素。

  4. 随机访问效率低:访问链表中的第n个元素需要从头开始遍历,时间复杂度为O(n)。

  5. 额外的内存开销:每个节点除了存储数据外,还需要存储指向其他节点的指针。

2. 链表的类型

链表根据节点之间的连接方式,可以分为四种主要类型:单向链表、双向链表、循环链表和双向循环链表。

2.1 单向链表

单向链表是最基本的链表形式,每个节点包含数据和一个指向下一个节点的指针。链表的最后一个节点的指针指向NULL,表示链表的结束。

在这里插入图片描述

特点

  • 只能从头到尾遍历
  • 每个节点只有一个指针
  • 只能访问当前节点的下一个节点
  • 内存开销较小

C++节点定义

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

2.2 双向链表

双向链表中的每个节点包含数据和两个指针:一个指向前一个节点,一个指向后一个节点。链表的第一个节点的prev指针和最后一个节点的next指针都指向NULL。

特点

  • 可以双向遍历(从头到尾或从尾到头)
  • 每个节点有两个指针
  • 可以访问当前节点的前一个和后一个节点
  • 内存开销较大
  • 插入和删除操作更灵活

结构示意图

NULL ← [prev|数据|next] ⇄ [prev|数据|next] ⇄ [prev|数据|next] → NULL↑head

C++节点定义

struct Node {int data;       // 数据域,存储节点的值Node* prev;     // 前驱指针,指向前一个节点Node* next;     // 后继指针,指向后一个节点
};

2.3 循环链表

循环链表是单向链表的变体,其中最后一个节点的指针不是指向NULL,而是指向链表的第一个节点,形成一个环。

特点

  • 没有明确的结束点
  • 可以从任何节点开始遍历整个链表
  • 适用于需要循环处理的场景,如轮询调度

结构示意图

    ┌───────────────────────────┐↓                           |
head → [数据|next] → [数据|next] → [数据|next]

C++节点定义

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

2.4 双向循环链表

双向循环链表结合了双向链表和循环链表的特点。第一个节点的prev指针指向最后一个节点,最后一个节点的next指针指向第一个节点。

特点

  • 可以双向循环遍历
  • 每个节点都可以访问其前一个和后一个节点
  • 没有明确的开始和结束点
  • 内存开销最大
  • 最灵活的链表结构

结构示意图

    ┌─────────────────────────────────────┐|                                     |↓                                     ↑
[prev|数据|next] ⇄ [prev|数据|next] ⇄ [prev|数据|next]↑                                     |└─────────────────────────────────────┘

C++节点定义

struct Node {int data;       // 数据域,存储节点的值Node* prev;     // 前驱指针,指向前一个节点Node* next;     // 后继指针,指向后一个节点
};

3. C++中的链表实现

在C++中,我们可以使用结构体或类来实现链表。下面将详细介绍各种链表的C++实现。

3.1 节点结构定义

链表的基本组成单位是节点,我们首先需要定义节点的结构:

// 单向链表节点结构
struct Node {int data;       // 数据域,存储节点的值Node* next;     // 指针域,指向下一个节点// 默认构造函数Node() : data(0), next(nullptr) {}// 带参数的构造函数Node(int val) : data(val), next(nullptr) {}
};// 双向链表节点结构
struct DNode {int data;       // 数据域,存储节点的值DNode* prev;    // 前驱指针,指向前一个节点DNode* next;    // 后继指针,指向后一个节点// 默认构造函数DNode() : data(0), prev(nullptr), next(nullptr) {}// 带参数的构造函数DNode(int val) : data(val), prev(nullptr), next(nullptr) {}
};

3.2 单向链表实现

下面是一个完整的单向链表类实现,包括常见的操作如插入、删除、遍历等:

// 单向链表类
class SinglyLinkedList {
private:Node* head;     // 头指针,指向链表的第一个节点public:// 构造函数SinglyLinkedList() : head(nullptr) {}// 析构函数,释放所有节点内存~SinglyLinkedList() {Node* current = head;while (current != nullptr) {Node* temp = current;current = current->next;delete temp;}head = nullptr;}// 在链表头部插入节点void insertAtHead(int value) {// 创建新节点Node* newNode = new Node(value);// 如果链表为空,新节点就是头节点if (head == nullptr) {head = newNode;return;}// 否则,将新节点插入到头部newNode->next = head;head = newNode;}// 在链表尾部插入节点void insertAtTail(int value) {// 创建新节点Node* newNode = new Node(value);// 如果链表为空,新节点就是头节点if (head == nullptr) {head = newNode;return;}// 找到链表的最后一个节点Node* current = head;while (current->next != nullptr) {current = current->next;}// 将新节点连接到最后一个节点current->next = newNode;}// 在指定位置插入节点void insertAtPosition(int position, int value) {// 如果位置为0,相当于在头部插入if (position == 0) {insertAtHead(value);return;}// 创建新节点Node* newNode = new Node(value);// 找到要插入位置的前一个节点Node* current = head;for (int i = 0; i < position - 1 && current != nullptr; i++) {current = current->next;}// 如果位置超出链表长度,或链表为空if (current == nullptr) {std::cout << "位置无效" << std::endl;delete newNode;return;}// 插入新节点newNode->next = current->next;current->next = newNode;}// 删除头部节点void deleteFromHead() {// 如果链表为空,无法删除if (head == nullptr) {std::cout << "链表为空,无法删除" << std::endl;return;}// 保存头节点Node* temp = head;// 更新头指针head = head->next;// 释放原头节点的内存delete temp;}// 删除尾部节点void deleteFromTail() {// 如果链表为空,无法删除if (head == nullptr) {std::cout << "链表为空,无法删除" << std::endl;return;}// 如果链表只有一个节点if (head->next == nullptr) {delete head;head = nullptr;return;}// 找到倒数第二个节点Node* current = head;while (current->next->next != nullptr) {current = current->next;}// 删除最后一个节点delete current->next;current->next = nullptr;}// 删除指定位置的节点void deleteFromPosition(int position) {// 如果链表为空,无法删除if (head == nullptr) {std::cout << "链表为空,无法删除" << std::endl;return;}// 如果要删除头节点if (position == 0) {deleteFromHead();return;}// 找到要删除位置的前一个节点Node* current = head;for (int i = 0; i < position - 1 && current != nullptr && current->next != nullptr; i++) {current = current->next;}// 如果位置无效if (current == nullptr || current->next == nullptr) {std::cout << "位置无效" << std::endl;return;}// 保存要删除的节点Node* temp = current->next;// 更新链接current->next = temp->next;// 释放节点内存delete temp;}// 打印链表void display() {Node* current = head;if (current == nullptr) {std::cout << "链表为空" << std::endl;return;}std::cout << "链表内容: ";while (current != nullptr) {std::cout << current->data << " -> ";current = current->next

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

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

相关文章

了解图像质量评价指标PSNR

一、PSNR是什么 1.1 定义与数学公式 峰值信噪比&#xff08;Peak Signal-to-Noise Ratio&#xff0c;PSNR&#xff09;是数字图像处理领域最经典的客观质量评价指标之一。其核心思想是通过计算原始图像与失真图像之间的均方误差&#xff08;MSE&#xff09;来衡量失真程度&am…

NX二次开发刻字功能——布尔运算

刻字功能在经历、创建文本、拉伸功能以后就剩下布尔运算了。布尔运算的目的就是实现文本时凸还是凹。这部分内容很简单。 1、首先识别布尔运算的类型&#xff0c;我这里用到一个枚举类型的选项&#xff0c;凸就是布尔求和&#xff0c;凹就是布尔求差。 2、其放置位置为创建拉伸…

《C语言实现金字塔图案打印》

&#x1f680;个人主页&#xff1a;BabyZZの秘密日记 &#x1f4d6;收入专栏&#xff1a;C语言练习题分享 &#x1f30d;文章目入 程序代码程序功能程序分析外层循环内层循环输出结果 示例运行总结 在学习编程的过程中&#xff0c;打印图案是一个非常有趣的练习&#xff0c;它可…

Shiro学习(一):Shiro介绍和基本使用

一、Shiro介绍 1、百科对shiro的定义如下&#xff1a; Apache Shiro 一个强大且易于使用的 Java 安全框架&#xff0c;它提供了身份验证、授权、加密和会话管理等功能。Shiro 的设计目标是简化企业级应用程序的安全性开发过程&#xff0c;同时保持代码的简洁和易于维护。 2、…

Java多线程与高并发专题——关于Condition

Condition接口 源码注释 还是老样子&#xff0c;看看源码注释&#xff1a; Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with t…

JavaScript 性能优化实战:突破瓶颈,打造极致 Web 体验

在当今快节奏的互联网时代&#xff0c;用户对于 Web 应用的性能要求越来越高。一个响应迅速、流畅运行的 Web 页面能够极大地提升用户体验&#xff0c;反之&#xff0c;缓慢的加载速度和卡顿的交互则可能导致用户流失。JavaScript 作为 Web 开发的核心语言之一&#xff0c;其性…

《白帽子讲 Web 安全》之服务端请求伪造(SSRF)深度剖析:从攻击到防御

引言 在当今复杂的网络环境中&#xff0c;Web 应用安全犹如一座时刻需要精心守护的堡垒。随着技术的不断演进&#xff0c;各类安全威胁层出不穷&#xff0c;其中服务端请求伪造&#xff08;SSRF&#xff09;正逐渐成为令开发者与安全从业者头疼的一大难题。吴翰清在《白帽子讲…

Pandas的轴,axis=0,axis=1

八. Pandas的轴 axis0代表跨行&#xff08;down)&#xff0c;而axis1代表跨列&#xff08;across) 使用0值表示沿着每一列或行标签\索引值向下执行方法使用1值表示沿着每一行或者列标签模向执行对应的方法 下图代表在DataFrame当中axis为0和1时分别代表的含义: axis参数作用…

matplotlib学习

开始学习Python数据可视化 一.基础绘图函数 1.创建画布与坐标轴 import matplotlib.pyplot as plt# 创建画布和坐标轴 fig, ax plt.subplots() # 默认1行1列&#xff0c;返回Figure对象和Axes对象 2.绘制线图 x [1, 2, 3, 4] y [10, 20, 15, 25]# 绘制线图 ax.plot(x,…

系统架构设计前的多角度思考

首先&#xff0c;从需求分析入手&#xff0c;不仅关注当前功能&#xff0c;还要考虑业务未来的扩展方向。比如数据量预估增长多少&#xff1f;这些都是影响架构的重要因素。 然后是架构设计原则&#xff0c;比如分层设计、模块化、高内聚低耦合等。比如如何划分服务边界&#x…

leetcode230.二叉搜索树中第k小的元素

中序遍历&#xff0c;第k次出现的数值就是结果 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left…

运筹说 第134期 | 矩阵对策的解法

上一期我们了解了矩阵对策的基本理论&#xff0c;包含矩阵对策的纯策略、矩阵对策的混合策略和矩阵对策的基本定理。 接下来小编将为大家介绍矩阵对策的解法&#xff0c;包括图解法、方程组法和线性规划法三种经典方法。 01 图解法 本节首先介绍矩阵对策的图解法&#xff0c;…

Python贝叶斯分层模型专题|对环境健康、医学心梗患者、体育赛事数据空间异质性实证分析合集|附数据代码

全文链接&#xff1a;https://tecdat.cn/?p41267 在大数据时代&#xff0c;多水平数据结构广泛存在于环境健康、医学研究和体育赛事等领域。本专题合集聚焦贝叶斯分层模型&#xff08;Hierarchical Bayesian Model&#xff09;的创新应用&#xff0c;通过氡气污染数据与 季后…

NOI2015提高组.子串

题目 520. 子串 思路 设计状态表示 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示 a a a的前 i i i个字符, b b b的前 j j j个字符, 并且已经分割了 k k k个子串的所有方案, 将状态划分为包含第 i i i个字符和不包含第 i i i个字符, 不包含第 i i i个字符的状态是 f [ i…

医疗智能体通信整合-大模型训练中沟通优化策略研究

一、引言:医疗模型训练的沟通困境 1.1 医疗 AI 发展背景 在数智化浪潮的推动下,医疗 AI 正以前所未有的速度融入现代医疗体系。从智能影像诊断助力医生精准识别病灶,到基于大数据分析的个性化药物研发,医疗 AI 在提升医疗效率、改善医疗质量方面展现出巨大潜力。据相关数据…

存储管理(一)

目录 一、存储管理的功能 1.地址映射&#xff08;地址重定位&#xff09; 2.主存分配和回收 3.存储保护 4.主存扩充&#xff08;虚拟存储&#xff09; 二、程序的装入与链接 程序的装入&#xff1a; 程序的链接 三、连续分配方式 单一连续分配 固定分区分配 动态分…

SpringBoot学习笔记3.27

目录 实战篇第二课 1.注册参数的校验&#xff1a; 学习过程中遇到的问题&#xff1a; 1.什么是正则表达式 2.怎么自定义异常&#xff1f; 1. 创建全局异常处理类 2. 定义响应对象 3. 使用 ExceptionHandler 4. 设置响应状态码 5. 返回统一响应 6. 测试全局异常处理 …

基于springboot+vue的游戏账号交易系统的设计与实现

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

小测验——合并多个网格文件调用相机参数进行适配

文章目录 一、前言1.1 对于rule1.2 对于ask、agent、edit1.3 对于没有notepad二、代码展示一、前言 1.1 对于rule 对于.cursorrules里面的文件内容,就是从提示词、项目简介、技术架构、目录结构、代码规范这几方面进行介绍 1.2 对于ask、agent、edit 切换模式在聊天框下方…

敏捷测试(Agile Testing)

敏捷测试&#xff08;Agile Testing&#xff09; 敏捷测试是在敏捷开发&#xff08;Agile Development&#xff09;环境下进行的软件测试方法&#xff0c;强调快速反馈、持续测试、团队协作&#xff0c;以确保软件质量贯穿整个开发周期。与传统瀑布模型不同&#xff0c;敏捷测…