链表 复习

数据结构入门:链表超全解析

链表是线性表最常用的实现之一,完美解决了顺序表插入删除效率低、扩容浪费空间的问题。本文把课程里所有链表知识点完整整理

一、链表基本概念

链表是一种物理存储结构上非连续、非顺序 的存储结构,数据元素的逻辑顺序 通过链表中的指针链接次序实现。

核心特点:

  • 逻辑上连续,物理上不一定连续
  • 结点一般从堆上申请
  • 两次申请的空间可能连续,也可能不连续

二、链表的分类(8种组合)

链表按3个维度组合,一共 8 种结构

  1. 单向 / 双向
  2. 带头 / 不带头
  3. 循环 / 非循环

实际最常用两种

  1. 无头单向非循环链表

    • 结构简单
    • 一般不单独存数据,常作为哈希桶、图邻接表等子结构
    • 笔试面试高频
  2. 带头双向循环链表

    • 结构最复杂
    • 实际工程单独存数据几乎都用它
    • 实现反而更简单、操作更高效

三、无头单向非循环链表实现

1. 结点结构

c 复制代码
typedef int SLTDateType;
typedef struct SListNode
{
    SLTDateType data;
    struct SListNode* next;
}SListNode;

2. 核心接口声明

c 复制代码
// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x);

// 打印链表
void SListPrint(SListNode* plist);

// 尾插
void SListPushBack(SListNode** pplist, SLTDateType x);

// 头插
void SListPushFront(SListNode** pplist, SLTDateType x);

// 尾删
void SListPopBack(SListNode** pplist);

// 头删
void SListPopFront(SListNode** pplist);

// 查找
SListNode* SListFind(SListNode* plist, SLTDateType x);

// 在pos之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);

// 删除pos之后的值
void SListEraseAfter(SListNode* pos);

四、带头双向循环链表实现

1. 结点结构

c 复制代码
typedef int LTDataType;
typedef struct ListNode
{
    LTDataType _data;
    struct ListNode* next;
    struct ListNode* prev;
}ListNode;

2. 核心接口声明

c 复制代码
// 创建头结点
ListNode* ListCreate();

// 销毁
void ListDestory(ListNode* plist);

// 打印
void ListPrint(ListNode* plist);

// 尾插/尾删
void ListPushBack(ListNode* plist, LTDataType x);
void ListPopBack(ListNode* plist);

// 头插/头删
void ListPushFront(ListNode* plist, LTDataType x);
void ListPopFront(ListNode* plist);

// 查找
ListNode* ListFind(ListNode* plist, LTDataType x);

// 在pos前插入
void ListInsert(ListNode* pos, LTDataType x);

// 删除pos位置结点
void ListErase(ListNode* pos);

五、链表面试题(课程完整版)

  1. 删除链表中等于给定值 val 的所有结点
  2. 反转一个单链表
  3. 返回链表中间结点(偶数返回第二个)
  4. 输出倒数第 k 个结点
  5. 合并两个有序链表
  6. 以 x 为基准分割链表(小在前,大等于在后)
  7. 判断链表回文结构
  8. 找两个链表的第一个公共结点
  9. 判断链表是否有环
  10. 找到链表入环第一个结点
  11. 带随机指针的链表深度拷贝

六、链表环问题

1. 判断是否有环:快慢指针

  • 慢指针一次走 1 步
  • 快指针一次走 2 步
  • 有环一定会相遇;无环快指针先到 NULL

2. 为什么快指针只能走 2 步?

  • 快 3 步、慢 1 步:可能永远不相遇(刚好套圈错过)
  • 快 2 步、慢 1 步:一定相遇
  • 环最小长度为 1,即使套圈也会在同一点

3. 找环的入口结点(结论)

  • 一个指针从链表起点出发
  • 一个指针从相遇点出发
  • 均一次走 1 步,最终在环入口相遇

公式推导(课程版)

设:

  • H:起点
  • E:环入口
  • M:相遇点
  • L:H→E 长度
  • X:E→M 长度
  • R:环长

快慢指针路程:

  • fast:L + X + n×R
  • slow:L + X
  • 2×slow = fast
    → L + X = n×R
    → L = n×R − X

即:起点走 L 步 = 相遇点绕环走到入口。


七、顺序表 vs 链表(核心对比)

不同点 顺序表 链表
存储空间 物理一定连续 逻辑连续,物理不一定连续
随机访问 支持 O(1) 不支持 O(N)
插入/删除 需搬移元素 O(N) 只改指针,高效
容量 需扩容 无容量限制
缓存利用率
适用场景 频繁访问、少修改 频繁插入删除

总结

  • 链表物理不连续、靠指针相连,解决顺序表缺陷
  • 单向链表简单适合面试;双向循环链表适合工程使用
  • 快慢指针是链表环问题、中间节点、倒数节点的万能解法
  • 按需选择:频繁读取用顺序表,频繁增删用链表
相关推荐
刘马想放假3 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
北域码匠4 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
Darling噜啦啦10 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
小小工匠12 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
玖玥拾12 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
Qres82112 天前
算法复键——树状数组
数据结构·算法
牛油果子哥q12 天前
并查集(DSU)超精讲,路径压缩、按秩合并、万能模板、连通性判定、最小生成树与刷题实战全解
数据结构·c++·最小生成树·并查集
凌波粒12 天前
LeetCode--491.递增子序列(回溯算法)
数据结构·算法·leetcode
疯狂成瘾者12 天前
Java 集合 LinkedList 详解:链表结构、常用方法和队列使用
java·开发语言·链表
WL学习笔记12 天前
单项不带头不循环链表
数据结构·链表