链表 复习

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

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

一、链表基本概念

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

核心特点:

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

二、链表的分类(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) 只改指针,高效
容量 需扩容 无容量限制
缓存利用率
适用场景 频繁访问、少修改 频繁插入删除

总结

  • 链表物理不连续、靠指针相连,解决顺序表缺陷
  • 单向链表简单适合面试;双向循环链表适合工程使用
  • 快慢指针是链表环问题、中间节点、倒数节点的万能解法
  • 按需选择:频繁读取用顺序表,频繁增删用链表
相关推荐
Rabitebla2 小时前
【数据结构】消失的数字+ 轮转数组:踩坑详解
c语言·数据结构·c++·算法·leetcode
海清河晏1112 小时前
数据结构 | 顺序栈
数据结构
疯狂打码的少年2 小时前
数据结构图的存储方式:从邻接矩阵到十字链表,一文打尽
数据结构·链表
Queenie_Charlie2 小时前
关于二叉树(2)
数据结构·c++·二叉树·简单树结构
澈2072 小时前
算法进阶:二叉树翻转与环形链表解析
数据结构·算法·排序算法
代码飞天2 小时前
算法与数据结构之树——让数据查找更加迅速
数据结构·算法
故事和你912 小时前
洛谷-算法2-2-常见优化技巧1
开发语言·数据结构·c++·算法·动态规划·图论
酉鬼女又兒2 小时前
JavaLeetCode 第一题「两数之和」:从暴力枚举到一遍哈希表的正确与错误实现,详解HashMap核心知识点及常见陷阱
java·开发语言·数据结构·算法·leetcode·职场和发展·散列表
云淡风轻~窗明几净2 小时前
关于TSP的sealine算法与角谷猜想(2026-04-25)
数据结构·人工智能·算法·动态规划·模拟退火算法