Skip to content

数据结构与算法详细复习笔记

第2章:线性表 (Linear List)

1. 基本概念

  • 定义:由 n(n0) 个数据特性相同的元素构成的有限序列。
    • 特性
      • 有序性:元素之间有逻辑上的顺序(第一个、最后一个、前驱、后继)。
      • 有限性:元素个数有限。
  • 抽象数据类型 (ADT)
    • 数据对象:D={ai|aiElemSet,i=1,2,...,n}
    • 数据关系:R={<ai1,ai>|ai1,aiD,i=2,...,n}
    • 基本操作:InitList, DestroyList, ListInsert, ListDelete, GetElem, LocateElem 等。

2. 顺序表 (Sequential List)

  • 定义:用一组地址连续的存储单元依次存储线性表的数据元素。
  • 特点
    • 随机存取:通过首地址和下标可在 O(1) 时间内访问任意元素。公式:Loc(ai)=Loc(a1)+(i1)×L
    • 存储密度高:无需为逻辑关系(指针)额外分配空间。
    • 缺点:插入/删除需要移动大量元素(平均移动 n/2),需预分配空间(静态分配易溢出,动态分配需扩容)。

3. 链表 (Linked List)

  • 单链表 (Singly Linked List)
    • 结构:结点 = 数据域 (data) + 指针域 (next)。
    • 头结点:在首元结点前附设的一个结点,便于处理空表和统一插入/删除操作。
    • 建立方法
      • 头插法:新结点插入头结点之后,读入顺序与链表顺序相反(逆序)。
      • 尾插法:维护一个尾指针 r,新结点插在 r 之后,读入顺序与链表顺序相同。
  • 双向链表 (Doubly Linked List)
    • 结构:结点包含 prior (前驱) 和 next (后继) 指针。
    • 插入操作 (s 插在 p 之后):
      1. s->prior = p;
      2. s->next = p->next;
      3. p->next->prior = s;
      4. p->next = s; (注意顺序,防止断链)
    • 删除操作 (删除 p 的后继节点 q):
      1. p->next = q->next;
      2. q->next->prior = p;
      3. free(q);
  • 循环链表 (Circular Linked List)
    • 特点:表中最后一个结点的指针指向头结点,形成环状。
    • 判空/遍历结束:指针是否等于头指针 (or 尾指针)。
    • 优势:从任意结点出发均可遍历全表;若设尾指针 rear,则查找头尾的时间均为 O(1)(便于合并两个链表)。
  • 静态链表
    • 利用数组实现,元素包含 datacur (游标,即下个元素的数组下标)。
    • 适用于不支持指针的语言(如 BASIC)或数据量固定且需频繁操作指针的场景。
  • 块状链表 (Unrolled Linked List)
    • 每个结点存储多个数据元素(即一个小的顺序表)。平衡了顺序表(空间紧凑)和链表(插入删除快)的优缺点。

4. 应用

  • 一元多项式相加:利用链表按指数升序存储。遍历两个链表,比较指数大小:
    • 指数相同:系数相加,若非零则保留。
    • 指数不同:将指数小的项链入结果链表。
  • 大整数处理:利用链表存储大整数的每一位(或每几位),便于进位处理。

第3章:栈与队列 (Stack & Queue)

1. 栈 (Stack)

  • 定义:只允许在表尾(栈顶 Top)进行插入(Push)和删除(Pop)的线性表。LIFO (Last In First Out)。
  • 实现
    • 顺序栈top 指针指向栈顶元素(或栈顶上一个空位)。需判断 StackOverflow (上溢) 和 StackUnderflow (下溢,即空栈 pop)。
    • 共享栈:两个栈共享一段空间,栈底在两端,栈顶向中间延伸,top1 + 1 == top2 时满。
  • 应用
    • 递归:系统栈保存每一层调用的局部变量、返回地址。
    • 括号匹配:左括号压栈,右括号弹栈匹配。
    • 表达式求值
      • 中缀转后缀 (RPN):遇操作数输出;遇运算符与栈顶比较优先级。
      • 后缀计算:遇操作数压栈;遇运算符弹栈计算结果并压栈。
    • 迷宫求解:DFS 深度优先搜索(回溯法)。

2. 队列 (Queue)

  • 定义:只允许在表尾(Rear)插入,表头(Front)删除。FIFO (First In First Out)。
  • 循环队列 (Circular Queue)
    • 问题:顺序队列单纯 front++ rear++ 会导致“假溢出”(即前方有空位但 rear 已达上限)。
    • 解决:模运算。(rear + 1) % MAXSIZE
    • 判空/判满:为了区分队空和队满,通常牺牲一个单元。
      • 队空front == rear
      • 队满(rear + 1) % MAXSIZE == front
      • 队长(rear - front + MAXSIZE) % MAXSIZE
  • 双端队列 (Deque):两端均可入队、出队。
    • 变种:输入受限(一端入两端出)、输出受限(两端入一端出)。

第4章:字符串 (String)

1. 基本概念

  • :零个或多个字符组成的有限序列。
  • 子串:主串中任意连续字符组成的子序列。
  • 存储:定长顺序存储(截断)、堆分配存储(动态指针)、块链存储。

2. 模式匹配算法

  • 朴素模式匹配 (Brute-Force)
    • 主串指针 i,模式串指针 j
    • 失配时:i 回溯到 i - j + 1j 回溯到 0
    • 最坏时间复杂度:O(n×m)
  • KMP 算法 (Knuth-Morris-Pratt)
    • 核心:主串指针 i 不回溯,模式串指针 j 回退到 next[j]
    • Next 数组next[j] 表示模式串第 j 个字符前的子串中,最长相等前后缀的长度
      • S[i] != P[j] 时,j 移动到 next[j] 继续比较。
    • NextVal 数组(优化):若 P[j] == P[next[j]],则递归优化,避免重复无效比较。
    • 时间复杂度:O(n+m)

第5章:数组和广义表

1. 数组

  • 存储:内存是一维的,多维数组需映射。
    • 行优先 (Row-Major):C/C++。LOC(a[i][j]) = LOC(0,0) + (i * n + j) * size
    • 列优先 (Column-Major):Fortran。

2. 矩阵压缩存储

  • 对称矩阵aij=aji。只存上三角或下三角。按行优先存储下三角元素 (ij) 的下标 k=i(i+1)2+j
  • 稀疏矩阵 (Sparse Matrix)
    • 三元组表(i, j, value)。空间优化,但丧失随机存取特性。
    • 十字链表:每个非零元节点既在行链表上,又在列链表上。便于频繁插入删除。

3. 广义表 (Generalized List)

  • 定义LS=(a1,a2,...,an),其中 ai 可以是原子或子表。
  • 重要操作
    • GetHead(L):取表头(第一个元素,可能是原子或列表)。
    • GetTail(L):取表尾(除第一个元素外剩余元素构成的表,结果一定是一个表)。
    • L=((a,b),c),Head(L)=(a,b),Tail(L)=(c)

第6章:树与二叉树

1. 二叉树 (Binary Tree)

  • 性质
    1. i 层至多 2i1 个结点。
    2. 深度为 k 至多 2k1 个结点。
    3. 核心性质n0=n2+1(叶子结点数 = 度为2的结点数 + 1)。
  • 完全二叉树 (Complete Binary Tree)
    • 编号规则与满二叉树一一对应。
    • 数组存储时,结点 i 的左孩子为 2i,右孩子为 2i+1,父节点 i/2

2. 遍历 (Traversal)

  • 前序 (Pre):根 -> 左 -> 右
  • 中序 (In):左 -> 根 -> 右
  • 后序 (Post):左 -> 右 -> 根
  • 层序:利用队列实现 BFS。
  • 构造
    • 前序 + 中序 -> 唯一确定二叉树。
    • 后序 + 中序 -> 唯一确定二叉树。
    • 前序 + 后序 -> 不能唯一确定(无法区分左右子树)。

3. 应用

  • 哈夫曼树 (Huffman Tree)
    • 构造:每次选取权值最小的两棵树合并,新树权值为两者之和,直到只剩一棵。
    • WPL (带权路径长度)(×)
    • 哈夫曼编码:前缀编码(任一字符编码都不是另一字符编码的前缀),用于数据压缩。
  • 线索二叉树
    • 利用空闲指针域(lchild若空指向前驱,rchild若空指向后继),通过 ltag/rtag 标记。
    • 加快遍历速度,无需递归或栈。

第7章:图 (Graph)

1. 存储结构

  • 邻接矩阵AdjMatrix[i][j] = 1 或权值。
    • 优点:易判断两点是否相连,易算度(行和列和)。
    • 缺点:稀疏图浪费空间 O(n2)
  • 邻接表:顶点表 + 边表(链表)。
    • 优点:节省空间 O(n+e)
    • 缺点:求入度麻烦(需遍历全表或使用逆邻接表)。

2. 遍历

  • DFS (深度优先):类似树的先序遍历,使用递归/栈
  • BFS (广度优先):类似树的层序遍历,使用队列。最短路径特性(无权图)。

第8章:图的应用 (重难点)

1. 最小生成树 (MST)

  • Prim 算法
    • 思想:归并点。从一个顶点开始,每次选一条连接“已选集合”和“未选集合”且权值最小的边。
    • 复杂度O(n2),适合稠密图
  • Kruskal 算法
    • 思想:归并边。将边按权值排序,从小到大选边,若不构成回路(利用并查集判断)则加入。
    • 复杂度O(eloge),适合稀疏图

2. 最短路径

  • Dijkstra 算法
    • 单源最短路。贪心策略。每次从未标记节点中选距离源点最近的节点,松弛其邻接点。
    • 限制:边权不能为负。
    • 复杂度O(n2)
  • Floyd 算法
    • 多源最短路。动态规划。A[i][j] = min(A[i][j], A[i][k] + A[k][j])
    • 复杂度O(n3)

3. AOV 网与拓扑排序

  • 定义:顶点表示活动,边表示优先关系。
  • 算法
    1. 选择入度为 0 的顶点输出。
    2. 删除该顶点及其出边。
    3. 重复直到图空或无入度为 0 的点(说明有环)。

4. AOE 网与关键路径

  • 定义:边表示活动,边权为持续时间,顶点表示事件。
  • 关键路径:从源点到汇点的最长路径(决定工程最短工期)。
  • 计算
    • 事件最早发生时间 ve(从前向后取max)。
    • 事件最晚发生时间 vl(从后向前取min)。
    • 活动最早/最晚开始时间 e / l
    • e == l,则为关键活动。

1. 动态查找树

  • 二叉排序树 (BST):左 < 根 < 右。中序遍历有序。插入/删除/查找平均 O(logn),最坏 O(n)(退化为链表)。
  • 平衡二叉树 (AVL)
    • 任一节点左右子树高度差(平衡因子 BF)绝对值 1
    • 调整
      • LL型:右旋(Right Rotation)。
      • RR型:左旋(Left Rotation)。
      • LR型:先左旋后右旋。
      • RL型:先右旋后左旋。

2. 散列 (Hashing)

  • 散列函数H(key)。除留余数法 H(key) = key % p (p 为不大于表长的最大质数)。
  • 处理冲突
    • 开放定址法
      • 线性探测:di = 1, 2, 3... (易产生聚集/堆积现象)。
      • 二次探测:di = 1^2, -1^2, 2^2, -2^2...
    • 链地址法:相同 Hash 值的元素存入同一链表。
  • ASL (平均查找长度):衡量效率指标,依赖于装填因子 α=

第10章:排序 (Sorting)

1. 算法复杂度与稳定性对比

类别排序算法平均时间最坏时间空间稳定性备注
插入直接插入O(n2)O(n2)O(1)稳定适合基本有序/n较小
希尔排序O(n1.3)O(n2)O(1)不稳定增量 d 逐渐减半
交换冒泡排序O(n2)O(n2)O(1)稳定标志位优化
快速排序O(nlogn)O(n2)O(logn)不稳定内排序最快,枢轴选择关键
选择简单选择O(n2)O(n2)O(1)不稳定移动次数少
堆排序O(nlogn)O(nlogn)O(1)不稳定适合 Top K 问题
归并归并排序O(nlogn)O(nlogn)O(n)稳定空间换时间,外排序基础
基数基数排序O(d(n+r))O(d(n+r))O(r)稳定无需比较,分配+收集

2. 核心算法说明

  • 快速排序 (Quick Sort)
    • Partition:选取枢轴(pivot),将小于 pivot 的放左边,大于的放右边。
    • 最坏情况:有序或逆序(退化为冒泡)。
  • 堆排序 (Heap Sort)
    • 建堆:从 n/2 处开始向下调整 (Heapify)。
    • 排序:交换堆顶与末尾元素,堆大小减1,重新向下调整。
    • 升序排序使用大顶堆
  • 归并排序
    • Divide (分):将数组对半切分。
    • Conquer (治):递归排序子数组。
    • Merge (合):合并两个有序数组。


附录:数据结构小测涉及公式与重点总结

通过分析《小测1 答案》、《小测2 答案》及《小测3_题目答案》,以下是针对考试重点涉及的公式、推导及核心结论的详细总结。

1. 栈与队列 (Stack & Queue)

卡特兰数 (Catalan Number)

  • 应用场景n 个不同元素进栈,可能的出栈序列总数。
  • 公式Cn=1n+1C2nn=(2n)!(n+1)!n!
  • 小测案例 (小测1 第6题):
    • n=3 时,出栈序列数为 C3=C634=204=5 种。

双端队列输出序列

  • 排除法逻辑 (小测1 第8题):
    • 对于输入受限或输出受限的双端队列,通常通过模拟和排除法来判断非法序列。
    • 核心技巧:结合栈的后进先出特性与队列的先进先出特性进行模拟。

2. 数组与矩阵 (Arrays & Matrices)

三对角矩阵压缩存储

  • 定义:元素仅分布在主对角线及上下两条对角线上。
  • 下标映射公式 (小测1 第10题):
    • 题目特定映射关系:A[1..n][1..n] 压缩至 B[1..3n2]
    • 给定公式推导结果:k=2i+j2
    • 案例验证A[66][65] 对应 k=2×66+652=195
    • 注意:通用公式通常为 k=2i+j3 或类似,考试时需严格根据题目给定的数组下标起始点(如从0还是从1开始)及排列方式进行推导。

KMP 算法 Next 数组计算

  • 推导逻辑 (小测1 第13题):
    • S[j]==S[k] (其中 k=next[j1]),则 next[j]=k+1
    • 若不等,则令 k=next[k] 继续回溯比较。
    • 案例S=aaab
      • j=1,S=a,next[1]=0 (固定)
      • j=2,S=a,S[1]==S[0]? (前缀比较), next[2]=1
      • j=3,S=a,S[2]==S[1]next[3]=2
      • j=4,S=b,S[3]S[2], 回溯...

3. 树与二叉树 (Trees)

二叉树节点性质公式

  1. 度数关系

    • n0=n2+1$$(=2+1)
      • 总节点数 n=n0+n1+n2
      • 总分支数 B=n1=n1+2n2
      • 联立消去 n1,得 n0=n2+1
    • 小测陷阱 (小测2 第1题):已知总数 n=98n1=48,求叶子数。
      • n0+48+n2=98n0+n2=50
      • 代入 n0=n2+12n2+1=502n2=49
      • n2 必须为整数,故不存在这样的树
  2. 完全二叉树 (Complete Binary Tree)

    • 叶子结点数:$$n_0 = \lceil n/2 \rceil$$ (当 n 为奇数时 n0=(n+1)/2,偶数时 n0=n/2)。
    • 案例 (小测2 第3题):n=47 (奇数),n0=(47+1)/2=24
  3. 哈夫曼树 (Huffman Tree)

    • 特性:只有度为0和度为2的结点,不存在度为1的结点 (n1=0)。
    • 节点总数公式n=2n01
    • 推导n=n0+n2,且 n2=n01n=n0+(n01)=2n01
    • 小测结论 (小测2 第12题):若有 n 个叶子结点(题目用 n 表示 n0),则总结点数为 2n1
  4. 森林与树的转换

    • 连通分量/树的数量计算 (小测2 第9题):
      • 若森林有 N 个结点,K 条边,则森林中树的棵数 T 为:T=NK
      • 推导:每棵树 (连通分量) 的边数 = 节点数 - 1。设第 i 棵树有 vi 个节点,则边数 ei=vi1
      • 总边数 K=(vi1)=vi1=NTT=NK

4. 图 (Graphs)

握手定理 (Handshaking Lemma)

  • 公式:所有顶点的度数之和等于边数的2倍。vVdeg(v)=2|E|
  • 最少顶点数求解 (小测3 第2题):
    • 已知:|E|=16 (度数和32)。
    • 已知点:3个度4,4个度3。已知度数和 3×4+4×3=24
    • 剩余度数:3224=8
    • 约束:其余顶点度数 <3 (即最大为2)。
    • 目标:顶点数最少 剩余顶点度数尽可能大 设其余点度数均为2。
    • 剩余点数:8/2=4 个。
    • 总顶点数:3+4+4=11 个。

最小生成树 (MST)

  • 唯一性判定 (小测3 第8题):
    • 当无向连通图的最小生成树不唯一时,Prim 和 Kruskal 算法生成的结果可能不同。
    • 当最小生成树唯一时,两者结果必然相同。

关键路径 (Critical Path)

  • 计算方法 (小测3 第11题):
    • 路径长度 = 路径上所有活动(边)持续时间之和。
    • 关键路径 = 源点到汇点的最长路径
    • 案例:路径 V0V1V4V6V8
    • 长度计算:6+1+9+2=18

排序算法深度解析 (Sorting Algorithms Deep Dive)

本文档对常见的排序算法进行展开讲解,包含核心思想算法步骤过程演示复杂度分析代码逻辑


1. 插入排序类 (Insertion Sorts)

1.1 直接插入排序 (Direct Insertion Sort)

核心思想: 将数组分为“已排序区间”和“未排序区间”。每次从未排序区间取出一个元素,在已排序区间中从后向前扫描,找到合适位置插入。类似于抓扑克牌的过程。

算法步骤

  1. 默认第 1 个元素已排序。
  2. 取出第 2 个元素 temp,与第 1 个比较。若 temp < arr[1],则 arr[1] 后移,temp 插入开头。
  3. 重复直到所有元素归位。

过程演示 (排序 [5, 2, 4, 6, 1, 3]):

  • 初始:[5] | 2, 4, 6, 1, 3
  • 取 2:2 比 5 小,5 后移。 -> [2, 5] | 4, 6, 1, 3
  • 取 4:4 比 5 小,5 后移;4 比 2 大,停。 -> [2, 4, 5] | 6, 1, 3
  • ...以此类推。

核心代码逻辑 (C/C++)

c
void InsertSort(int A[], int n) {
    int i, j, temp;
    for (i = 1; i < n; i++) {       // 从第2个元素开始
        if (A[i] < A[i-1]) {        // 若小于前驱,需插入
            temp = A[i];            // 暂存当前元素
            for (j = i-1; j >= 0 && A[j] > temp; --j) {
                A[j+1] = A[j];      // 大于temp的元素后移
            }
            A[j+1] = temp;          // 插入
        }
    }
}

分析

  • 最好情况O(n)(已是有序,只需比较不移动)。
  • 最坏情况O(n2)(逆序)。
  • 稳定性稳定(因为 A[j] > temp 才移动,相等时不移动)。

1.2 希尔排序 (Shell Sort)

核心思想: 又称“缩小增量排序”。是直接插入排序的优化版。通过将数组按增量 d 分组,对每组进行直接插入排序,使数组“基本有序”,最后 d=1 时再进行一次直接插入。

为什么比直接插入快? 直接插入在基本有序时效率极高。希尔排序前期移动步长长,能快速消除逆序对;后期步长短但数组已基本有序,移动次数少。

过程演示: 数组 [8, 9, 1, 7, 2, 3, 5, 4, 6, 0]n=10

  1. d=5 (分5组: 8,3, 9,5, 1,4, 7,6, 2,0):
    • 组内排序后 -> [3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
  2. d=2 (分2组: 3,1,0,9,7, 5,6,8,4,2):
    • 组内排序后 -> [0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
  3. d=1 (整体直接插入):
    • 微调 -> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

分析

  • 复杂度:依赖于增量序列,平均约 O(n1.3)
  • 稳定性不稳定(相同元素可能分在不同组,跳跃式移动导致相对位置改变)。

2. 交换排序类 (Exchange Sorts)

2.1 冒泡排序 (Bubble Sort)

核心思想: 两两比较相邻记录,如果反序则交换,直到没有反序的记录为止。每趟遍历将最大的元素“浮”到最后(或最小的“沉”到最前)。

优化点: 设置 flag 变量。若某一趟遍历中没有发生任何交换,说明已经有序,直接结束算法。

分析

  • 最好情况O(n)(有序,第一趟 flag 未变直接退出)。
  • 最坏情况O(n2)
  • 稳定性稳定

2.2 快速排序 (Quick Sort) —— 最重要的排序算法

核心思想分治法 (Divide and Conquer)

  1. 选基准 (Pivot):通常选第一个元素。
  2. 划分 (Partition):将比基准小的移到左边,比基准大的移到右边。基准归位。
  3. 递归:对左右两个子序列重复上述过程。

Partition 过程详解 (双指针法): 设基准 pivot = A[low]low 指向首,high 指向尾。

  1. High 向左找小while (low < high && A[high] >= pivot) high--; 找到比 pivot 小的,移到 low 处 (A[low] = A[high])。
  2. Low 向右找大while (low < high && A[low] <= pivot) low++; 找到比 pivot 大的,移到 high 处 (A[high] = A[low])。
  3. 重复直到 low == high,将 pivot 放入 A[low]

代码片段

c
int Partition(int A[], int low, int high) {
    int pivot = A[low]; // 选第一个为基准
    while (low < high) {
        while (low < high && A[high] >= pivot) high--;
        A[low] = A[high];
        while (low < high && A[low] <= pivot) low++;
        A[high] = A[low];
    }
    A[low] = pivot;
    return low; // 返回基准位置
}

分析

  • 平均复杂度O(nlogn)
  • 最坏复杂度O(n2)。发生在有序逆序时,基准每次都选到最大/最小,退化为冒泡。
  • 空间复杂度O(logn) (递归栈的高度)。
  • 稳定性不稳定(Partition 过程中的远距离交换)。

3. 选择排序类 (Selection Sorts)

3.1 简单选择排序 (Simple Selection Sort)

核心思想: 第 i 趟从 A[i...n] 中选出最小的元素,与 A[i] 交换。

特点

  • 交换次数少:最多 n1 次(优于冒泡)。
  • 比较次数多:固定 n(n1)2 次。
  • 稳定性不稳定
    • 例子[5, 8, 5, 2, 9]。第一趟,第一个 5 会和 2 交换,跑到第二个 5 后面。

3.2 堆排序 (Heap Sort)

核心思想: 利用(完全二叉树)这种数据结构。

  • 大顶堆A[i]A[2i+1]A[i]A[2i+2]。用于升序排序。
  • 小顶堆:用于降序排序。

算法步骤

  1. 建堆:将无序数组构造成一个大顶堆。
    • 从最后一个非叶子结点 (n/21) 开始,从右至左,从下至上进行“下沉”调整 (HeapAdjust / sift_down)。
  2. 排序
    • 将堆顶元素(最大值)与堆底末尾元素交换。
    • 将剩余 n1 个元素重新调整为大顶堆。
    • 重复直至堆大小为 1。

下沉 (sift_down) 逻辑: 判断节点 k,其左孩子 2k+1,右孩子 2k+2。 若孩子比父节点大,则将最大的孩子与父节点交换,并继续向下追踪被交换的孩子节点。

分析

  • 复杂度O(nlogn)。建堆 O(n),调整 O(nlogn)
  • 优势:在 Top K 问题(选出前 K 大)中表现极佳。
  • 稳定性不稳定

4. 归并排序 (Merge Sort)

核心思想分治法

  1. :将数组从中间切开,递归切分,直到长度为 1。
  2. 治 (Merge):将两个有序的子数组合并成一个有序数组。

Merge 过程: 需申请一个辅助数组 B[]。 比较左半区 A[i] 和右半区 A[j]

  • A[i] <= A[j],将 A[i] 放入 Bi++
  • A[i] > A[j],将 A[j] 放入 Bj++
  • 最后将剩余元素复制进 B,再把 B 拷回 A

分析

  • 复杂度O(nlogn)
  • 空间复杂度O(n)(需要辅助数组,这是它最大的缺点)。
  • 稳定性稳定(Merge 时 A[i] <= A[j] 优先取左边,保证相对位置不变)。

5. 基数排序 (Radix Sort)

核心思想不基于比较的排序。利用“分配”和“收集”。 通常针对非负整数。将整数按位数切割成不同的数字,按每个位数分别比较。

步骤 (LSD - Least Significant Digit)

  1. 按个位排序:准备 10 个桶 (0-9)。遍历数组,按个位数字放入对应桶。按顺序收集回来。
  2. 按十位排序:基于上一步结果,按十位放入桶,再收集。
  3. ...直至最高位。

分析

  • 复杂度O(d(n+r))
    • d:最大数字的位数(决定遍历几趟)。
    • n:元素个数。
    • r:基数(如十进制 r=10,决定桶的个数)。
  • 稳定性稳定(必须稳定,否则高位排序会打乱低位的顺序)。

6. 总结对比表

算法平均时间最好最坏空间稳定性关键词
直接插入O(n2)O(n)O(n2)O(1)稳定摸牌、越有序越快
希尔O(n1.3)-O(n2)O(1)不稳定增量分组
冒泡O(n2)O(n)O(n2)O(1)稳定交换、flag优化
快速O(nlogn)O(nlogn)O(n2)O(logn)不稳定Partition、分治
简单选择O(n2)O(n2)O(n2)O(1)不稳定选最小
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定建堆、下沉
归并O(nlogn)O(nlogn)O(nlogn)O(n)稳定合并、空间换时间

上次更新于: