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
计算几何凸包旋转卡壳最近点对线段相交扫描线求交
查找二分查找二分边界旋转数组搜索二分答案三分查找

全排列

回溯与搜索 · 决策树 DFS

列出所有排列

给一组互不相同的元素 [1, 2, 3],列出它们的全部排列——即所有「谁在第 1 位、谁在第 2 位……」的顺序。n 个元素共有 n! = n × (n−1) × … × 1 个排列。这也是回溯的经典范例,和子集生成用的是同一套决策树可视化,只是决策的方式不同。

每个位置,从「剩下的」里挑一个

排列要求每个元素恰好用一次。所以在决策树上,每往下一层就为下一个位置从「还没用过」的元素里挑一个:根有 3 个选择(第 1 位放谁),选定后剩 2 个(第 2 位),再剩 1 个(第 3 位)——分支数逐层收窄 3 → 2 → 1,走到叶子(元素用光)就得到一个完整排列,3! = 6 个叶子对应 6 个排列。

这正是它和子集的对照:子集对每个元素做「选 / 不选」的二叉决策(元素可要可不要);排列在每个位置「从剩余里挑一个」做多叉决策(元素必用且不重复)。回溯的骨架完全一样——选一个 → 递归到底 → 撤销换下一个。「已经用过的元素直接跳过」就是这里的剪枝。

下面是 [1, 2, 3] 的完整排列决策树。点「下一步」逐步看:当前节点琥珀高亮、根到当前的路径连起来高亮(此刻已挑的元素);每条边标着这一步选了哪个元素;走到叶子就标绿、记录一个排列;一支走完就回溯去挑下一个剩余元素。右侧代码随每一步同步高亮。

选 1选 2选 3选 3选 2选 2选 1选 3选 3选 1选 3选 1选 2选 2选 1[1,2,3][1,3,2][2,1,3][2,3,1][3,1,2][3,2,1]

空排列:每个位置从「剩余未用元素」里挑一个

1function permute(nums: number[]): number[][] {
2 const res: number[][] = [];
3 const cur: number[] = [];
4 const used = new Array(nums.length).fill(false);
5 const backtrack = (): void => {
6 if (cur.length === nums.length) {
7 res.push([...cur]);
8 return;
9 }
10 for (let i = 0; i < nums.length; i++) {
11 if (used[i]) continue; // 跳过已用(剪枝)
12 used[i] = true;
13 cur.push(nums[i]); // 选 nums[i]
14 backtrack();
15 cur.pop(); // 撤销(回溯)
16 used[i] = false;
17 }
18 };
19 backtrack();
20 return res;
21}
元素1 2 3
当前排列[]
剩余{1,2,3}
已收集0 / 6
1 / 28

注意分支数 3 → 2 → 1 相乘正好是 3! = 6——这就是排列数为 n! 的直观来源。实现上通常用一个 used[] 标记谁已用过:进入一层前把选中的标为已用、递归、回来再撤销标记(回溯),保证每个元素在一条根到叶的路径上只出现一次。

排列在哪里用

穷举顺序:旅行商暴力解、任务排期、密码/字典序枚举。
回溯家族:排列与子集、组合、组合总和同属「决策树 DFS」,区别只在每步的选择集与剪枝规则。
去重排列(含重复元素)只需在同层跳过重复选择——依旧是同一棵决策树上的剪枝。

至此回溯与搜索大类有了三种视角:N 皇后(棋盘约束)、子集(二叉决策树)、全排列(多叉决策树)——同一套 DFS + 剪枝 + 回退,套在不同的选择结构上。