当前位置: 首页 > news >正文

【数据结构与算法基础】04. 线性表与链表详解(C++ 实战)

【数据结构与算法基础】04. 线性表与链表详解(C++ 实战)

  • 掌握线性表的两种实现:**顺序表(数组/顺序存储)链表(指针/链式存储)**的核心差异与适用场景。
  • 熟练实现单链表的增删改查、双向链表循环链表的指针操作。
  • 熟练应用链表技巧:翻转快慢指针找中点判环每 k 个一组翻转
  • 能够基于业务语义在随机访问多增删频繁之间做出合理选型。

(关注不迷路哈!!!)

文章目录

  • 【数据结构与算法基础】04. 线性表与链表详解(C++ 实战)
    • 一、数据结构核心概念与方法论
      • 1.1 数据处理的本质
      • 1.2 方法论(三步法)
      • 1.3 复杂度权衡
      • 1.4 数据结构的引入
    • 二、线性表概述与链表类型
      • 2.1 线性表的定义
      • 2.2 链表的类型
    • 三、线性表对于数据的增删查处理(以单向链表为例)
      • 3.1 增加操作
      • 3.2 删除操作
      • 3.3 查找操作
      • 3.4 复杂度总结
    • 四、线性表案例
      • 4.1 链表的翻转
      • 4.2 查找链表中间位置的结点数值
      • 4.3 判断链表是否有环
    • 五、链表高频算法题精要与模板
      • 5.1 单链表翻转(迭代,返回新头结点)
      • 5.2 快慢指针找中点(奇数长度场景)
      • 5.3 判断链表是否有环(Floyd 判圈)
      • 5.4 每 k 个节点一组翻转(打印/返回新头结点)
    • 六、 C++ 工程案例实践
      • 6.1 案例代码
      • 6.2 输出结果
      • 6.3 代码说明
      • 6.3 代码功能


一、数据结构核心概念与方法论

1.1 数据处理的本质

数据处理的本质可抽象为 3 个基本操作:增、删、查

  • :向数据结构中写入新数据,可细分为“在末尾增”和“在中间增”。
  • :移除数据,可细分为“在末尾删”和“在中间删”。
  • :按条件定位数据,可细分为“按位置/索引查找”和“按数值/特征查找”。
  • ”的本质可分解为先查后删再增,因此仍归约到上述三类操作。

1.2 方法论(三步法)

  1. 明确代码对数据先后进行了哪些操作。
  2. 找出其中对时间复杂度影响最大的“热点操作”。
  3. 选择能将该热点操作降至或接近 O(1) 的数据结构。

1.3 复杂度权衡

  • 空间换时间是常用策略。

  • 某些结构虽占用 O(n) 空间,但可将关键操作从 O(n) 降至 O(1),从而显著降低整体时间复杂度。

1.4 数据结构的引入

当有了一定数量的数据时,需要考虑以什么样的方式对这些数据进行组织,这就是数据结构

  • 如同幼儿园小朋友组织运动会站队有多种方式(站成一横排、站成方阵、围成大圆圈等)
  • 计算机处理大量数据时也需要考虑数据的组织方式,实际开发中经过验证且高效的常用数据结构有限,掌握这些就能成为合格软件工程师。

二、线性表概述与链表类型

2.1 线性表的定义

  • 线性表定义:线性表是由 n 个数据元素构成的有限序列,是最基础、最常用的数据结构之一。通常也叫作线性链表或者链表

  • 典型应用:排队/先到先得(可用队列建模)、日志/消息顺序处理多项式/稀疏数据等。

  • 在链表中存储的数据元素也叫作结点,每个结点(结点结构)包含:

    • 数据域:存放具体数据值;

    • 指针域:指向下一个结点(单链表)。

  • 链表的头指针

    • 在链表的最前面,通常会有个头指针用来指向第一个结点。
  • 链表的尾结点

    • 链表的最后一个结点,由于在它之后没有下一个结点,因此它的指针是个空指针(nullptr)。

2.2 链表的类型

类型 遍历方向 典型优点 典型缺点
单向链表 单向 实现简单、空间开销小 无法反向遍历;插入/删除需前驱
双向链表 双向 可双向遍历;删除任意结点更便捷 每结点多一个指针,空间开销更大
循环链表 单向循环 无明确尾;循环遍历方便 不慎易造成无限循环
双向循环链表 双向循环 两端与环上操作都便捷 实现与维护复杂度更高

选型要点(速记):

  • 只需单向、尾部频繁插入且不需回溯 → 单向链表
  • 需要前驱双向遍历双向链表
  • 需要循环语义(如约瑟夫、轮询) → 循环/双向循环链表

这些种类的链表都是以 单向链表为基础 进行的变种

在这里插入图片描述


三、线性表对于数据的增删查处理(以单向链表为例)

3.1 增加操作

  • 操作描述:在链表中某个结点之后插入一个新结点。例如在一个存储了 10 个同学考试成绩的链表中,在红色结点之后插入一个忘记存储的成绩。

  • 操作方法:把待插入结点的指针指向原指针的目标,把原来的指针指向待插入的结点。

  • 时间复杂度:O(1),但实际新增数据时通常会伴随查找动作,整体复杂度可能为 O(n)。

3.2 删除操作

  • 操作描述:删除链表中误操作放进的某个成绩样本结点。
  • 操作方法:如果待删除的结点为 b,把指向 b 的指针 (p.next),指向 b 的指针指向的结点(p.next.next)。
  • 时间复杂度:O(1),但查找要删除的结点可能需要 O(n),整体复杂度可能为 O(n)。

3.3 查找操作

  • 按位置序号查找
    • 操作描述:类似于数组中的 index,例如在一个按学号存储了 10 个同学考试成绩的链表中,查找学号等于 5 的同学的成绩。
    • 操作方法:从头开始,一个一个地遍历去查找,先找到学号为 1 的同学,再经过他跳转到学号为 2 的同学,直到找到学号为 5 的同学。
    • 时间复杂度:O(n)。
  • 按具体成绩查找
    • 操作描述:在一个存储了 10 个同学考试成绩的链表中,查找是否有人得分为 95 分。
    • 操作方法:判断第一个结点的值是否等于 95,如果不是,则通过指针去判断下一个结点的值是否等于 95,以此类推,直到把所有结点都访问完。
    • 时间复杂度:O(n)。

3.4 复杂度总结

链表在新增、删除数据理论上可在 O(1) 的时间复杂度内完成,但实际中新增或删除数据往往伴随查找动作,整体复杂度常为 O(n)。查找不管按位置还是按数值条件,都需遍历全部数据,时间复杂度为 O(n)。

  • 线性表真正的价值在于其按顺序存储数据,当数据元素个数不确定且需要经常进行数据的新增和删除时,链表比较合适;
  • 若数据元素大小确定且删除插入操作不多,数组可能更适合。
数据结构 按索引查找 末尾增/删 中间增/删 典型适用场景
数组 Array O(1) 末尾O(1)(均摊)
开头/中间O(n)
开头/中间O(n) 随机访问
固定大小
尾部频繁写入
链表 Linked List O(n) 尾插O(1)(有尾指针)
头插O(1)
任意位置O(n) 频繁在头尾插入/删除
无需随机访问

四、线性表案例

4.1 链表的翻转

  • 问题描述:给定一个链表,输出翻转后的链表,例如输入 1 -> 2 -> 3 -> 4 -> 5,输出 5 -> 4 -> 3 -> 2 -> 1。
  • 难点分析:数组翻转容易,因为数组在连续空间存储,可通过索引查找元素并交换完成翻转;而单向链表指针结构使数据通路有去无回,修改指针会导致后面数据失联。
  • 解决方法:构造三个指针 prev、curr 和 next,对当前结点、以及它之前和之后的结点进行缓存,再完成翻转动作。

4.2 查找链表中间位置的结点数值

  • 问题描述:给定一个奇数个元素的链表,查找出这个链表中间位置的结点的数值。
  • 解决方法
    • 暴力方法:先通过一次遍历去计算链表的长度,知道链表中间位置是第几个,接着再通过一次遍历去查找这个位置的数值。
    • 巧妙方法(快慢指针):利用快慢指针进行处理,快指针每次循环向后跳转两次,而慢指针每次向后跳转一次。当快指针到达末尾时,慢指针指向中点。

4.3 判断链表是否有环

  • 问题描述:判断一个链表是否有环,例如图中所示的有环链表。
  • 解决方法:使用快慢指针方法。假设链表有环,快指针每次走两格,慢指针每次走一格,快指针每次循环会多走一步,所以如果链表存在环,快指针和慢指针一定会在环内相遇(fast == slow);反之,则最终会完成循环,二者从未相遇。

五、链表高频算法题精要与模板

5.1 单链表翻转(迭代,返回新头结点)

  • 三指针:prev = nullptr, curr = head, next = nullptr
  • 循环体:保存 next;反转指针;移动 prev/curr。
  • 时间复杂度:O(n);空间复杂度:O(1)
struct ListNode {int val;ListNode *next;ListNode(int x) : val(x), next(nullptr) {}
};ListNode* reverseList(ListNode* head)
{ListNode *prev = nullptr, *curr = head;while (curr){ListNode* next = curr->next;curr->next = prev;prev = curr;curr = next;}return prev;
}

在这里插入图片描述

5.2 快慢指针找中点(奇数长度场景)

  • fast 每次走 2 步,slow 每次走 1 步;fast 到末尾时,slow 指向中点。
  • 时间复杂度:O(n);空间复杂度:O(1)
ListNode* findMiddle(ListNode* head)
{if (!head) return nullptr;ListNode *slow = head, *fast = head;while (fast->next && fast->next->next){slow = slow->next;fast = fast->next->next;}return slow; // 奇数:正中;偶数:上中
}

在这里插入图片描述

5.3 判断链表是否有环(Floyd 判圈)

  • 快慢指针同启;若存在环,必相遇(fast == slow);否则快指针先到链表尾。
  • 时间复杂度:O(n);空间复杂度:O(1)
bool hasCycle(ListNode *head)
{if (!head || !head->next) return false;ListNode *slow = head, *fast = head;while (fast && fast->next){slow = slow->next;fast = fast->next->next;if (slow == fast) return true;}return false;
}

在这里插入图片描述

5.4 每 k 个节点一组翻转(打印/返回新头结点)

  • 思路:外部循环按组推进,组内复用“链表翻转”;组间用前驱指针串联结果。
  • 边界:k 合法性、链表长度不是 k 的整数倍时的处理策略(按题意决定最后一组是否翻转)。
ListNode* reverseKGroup(ListNode* head, int k)
{if (k <= 1) return head;ListNode dummy(0);dummy.next = head;ListNode *prev = &dummy;while (ListNode* grpHead = prev->next){ListNode *tail = grpHead;int step = 1;while (tail && step < k){tail = tail->next;++step;}if (!tail) break; // 不足 k 个,不再翻转ListNode *nextGrp = tail->next;tail->next = nullptr; // 断开当前组prev->next 
http://www.dtcms.com/a/502830.html

相关文章:

  • C程序中的预处理器
  • 长沙黄页全域seo
  • 负载均衡技术:Nginx/HAProxy/F5 等负载均衡配置与优化
  • 外国人做的关于中国的视频网站吗高师院校语言类课程体系改革与建设 教学成果奖申报网站
  • Linux 进阶指令实操指南:文件查看、时间管理、搜索压缩全场景覆盖(附高频案例)
  • K8S(十六)—— K8S集群apiserver证书有效期修改指南(适配v1.20.11版本)
  • Altium Designer(AD24)Reports报告功能总结
  • 第一章 绪论——课后习题解练【数据结构(c语言版 第2版)】
  • Ubuntu 系统 RabbitMQ 安装指南与使用(含 C++ 客户端与 SSL 错误解决)
  • 网站开发外包 价格阿里巴巴国际站入驻费用及条件
  • MVVM架构模式详解:从原理到Android实战
  • 【Pico企业版】Pico企业版的多种Wifi快速连接方式(Pico 4UE的快捷Wifi连接技巧)
  • Kafka服务端处理producer请求原理解析
  • 以电商系统为例,理解用户体验五层模型
  • 兰州网站开发企业学校门户网站建设
  • CreArt2.5.7 | 无限AI图片生成,将文字描述转化为艺术作品
  • Linux企业级解决方案架构:字节跳动短视频推荐系统全链路实践
  • Python编程之常用模块
  • ios面试底层题目
  • h5游戏免费下载:《高达战争》
  • 百度网盘不限速2025年最新方法
  • 网站维护大概要多久学校英文网站建设申请
  • 深入比较 Rust 与 Go:并发时代的两把利剑
  • 容器安全:gVisor系统调用过滤,攻击面缩小?
  • 前端基础二、CSS(二)、CSS基础选择器
  • 学做电影网站asp.net门户网站项目怎么做
  • 云主机搭建多个网站专业型网站网站
  • 【Linux学习笔记】线程同步与互斥之生产者消费者模型
  • C++函数使用
  • 立创EDA专业版使用技巧——全部框选与部分框选