V

算法可视化

ZHEN
分享到微博分享到 XGitHub 仓库个人主页
学习工具算法复杂度速查算法学习路径
数据结构数组链表栈队列树堆哈希表图字典树并查集LRU 缓存跳表线段树B+ 树布隆过滤器树状数组
经典排序算法冒泡排序鸡尾酒排序双调排序选择排序插入排序二分插入排序希尔排序归并排序自顶向下归并快速排序三路快排双轴快排堆排序计数排序基数排序桶排序
图算法Dijkstra 最短路Kruskal 最小生成树Prim 最小生成树Bellman-Ford 最短路拓扑排序Floyd 多源最短路强连通分量2-SAT最大流二分图匹配LCA 倍增欧拉路径
动态规划编辑距离0-1 背包完全背包最长公共子序列最长递增子序列硬币找零方案数石子合并旅行商 TSP树形 DP数位 DP换根 DP
回溯与搜索N 皇后子集生成全排列组合总和迷宫寻路岛屿数量单词搜索数独A* 寻路
字符串KMP 字符串匹配Rabin-KarpBoyer-MooreManacher后缀数组LCP / height 数组AC 自动机Z 函数
数学与数论埃氏筛线性筛欧几里得算法快速幂扩展欧几里得中国剩余定理欧拉函数米勒-拉宾FFTPollard's Rho
计算几何凸包旋转卡壳最近点对线段相交扫描线求交
查找二分查找二分边界旋转数组搜索二分答案三分查找

选择排序 Selection Sort

选择排序 · 交换次数最少

什么是选择排序

选择排序的思路像「每次挑最小的先站队」:在未排序区间里扫一遍、找出最小值,把它换到未排序区的最前面,这个位置就此定下来。然后在剩下的区间里再找次小的……前缀有序区从左往右一格格长大。它和冒泡都是 O(n²),但有个鲜明特点——每一轮最多只交换一次,总交换次数只有 n-1 次。

它怎么做

第 i 轮:从下标 i 往右扫,用 minIndex 记住当前最小值的位置(扫描时被记录的最小值高亮),扫完把 a[i] 和 a[minIndex] 交换。点「下一步」看 [7, 6, 5, 10, 9, 8, 4, 3, 2, 1]:注意整轮都在「找」、只有轮末那一下才「换」——比较很多、交换极省。

7
6
5
10
9
8
4
3
2
1

第 1 轮:先假定位置 0 最小

1function selectionSort(a: number[]): number[] {
2 const n = a.length;
3 for (let i = 0; i < n - 1; i++) {
4 let minIdx = i;
5 for (let j = i + 1; j < n; j++) {
6 if (a[j] < a[minIdx]) {
7 minIdx = j;
8 }
9 }
10 if (minIdx !== i) {
11 [a[i], a[minIdx]] = [a[minIdx], a[i]];
12 }
13 }
14 return a;
15}
n10
i0
j0
minIdx0
a[j]7
a[minIdx]7
swapCount0
sortedUpTo0
1 / 131

复杂度与适用

时间恒为 O(n²)(无论数据是否有序都要扫满)、常数额外空间、不稳定(跨越式交换会打乱相等元素的相对次序)。它的比较次数和冒泡一样多,但交换/写入次数最少——当「写」的代价远高于「比较」(如元素很大、或写入闪存有损耗)时,这个特性有实际意义。

选择 vs 冒泡:比较次数相同,但选择每轮只换一次、冒泡边扫边换。
选择的高效版:堆排序——用二叉堆在 O(log n) 内「选出最大值」,把 O(n²) 降到 O(n log n)。

当你觉得「每次都线性扫描找最值太慢」,就该看堆排序怎么用堆结构加速这个「选择」动作——它正是选择排序的进化终点。