LC46 全排列
Permutation Quest:全排列竞技场
把 1、2、3 三个英雄安排到 3 个座位上,找出所有不同出场顺序。这题不是在问“选哪几个数字”,而是在问“所有可能的顺序”。
3! = 6 种排列
输入与目标
输入
[1, 2, 3]
目标
列出所有可能的出场顺序
答案
[123], [132], [213], [231], [312], [321]
为什么用回溯
每一层递归负责一个位置:depth 0 填第 1 个位置,depth 1 填第 2 个位置,depth 2 填第 3 个位置,depth 3 表示 path 满了,可以收集答案。回溯的本质是:做选择 -> 进入下一层 -> 收集答案 -> 撤销选择 -> 换下一个选择。
path
当前队伍
used
谁已上场
depth
正在填的位置
res
结果墙
Step 1 / 36
Speed
Candidate Pool
候选英雄池
Hero 1
1
available
Hero 2
2
available
Hero 3
3
available
Recursion Tower
递归深度塔
depth 0:选择第 1 个位置d=0
depth 1:选择第 2 个位置d=1
depth 2:选择第 3 个位置d=2
depth 3:path 满了,收集答案d=3
path.length == nums.length · res.push([...path])
Permutation Arena
排列竞技场
初始化
初始化:候选英雄池就绪
depth 0
slot 0_
slot 1_
slot 2_
nums=[1,2,3],path=[],used=[false,false,false]。每一步都从当前 step 派生,避免 path、used 和结果墙不同步。
path = []used = [F,F,F]res = 0/6
Result Wall
Collected 0 / 6
#1empty
#2empty
#3empty
#4empty
#5empty
#6empty
Search Tree
搜索树游戏地图
root
├─ 1
│ ├─ 2
│ │ └─ 3 => [1,2,3]
│ └─ 3
│ └─ 2 => [1,3,2]
├─ 2
│ ├─ 1
│ │ └─ 3 => [2,1,3]
│ └─ 3
│ └─ 1 => [2,3,1]
└─ 3
├─ 1
│ └─ 2 => [3,1,2]
└─ 2
└─ 1 => [3,2,1]
Code Sync
代码同步区
1function permute(nums: number[]) {2 const res: number[][] = []3 const path: number[] = []4 const used = Array(nums.length).fill(false)56 function backtrack() {7 if (path.length === nums.length) {8 res.push([...path])9 return10 }1112 for (let i = 0; i < nums.length; i++) {13 if (used[i]) continue1415 path.push(nums[i])16 used[i] = true1718 backtrack()1920 path.pop()21 used[i] = false22 }23 }2425 backtrack()26 return res27}
当前动作
初始化
path
[]
used
[F,F,F]
why
nums=[1,2,3],path=[],used=[false,false,false]。每一步都从当前 step 派生,避免 path、used 和结果墙不同步。
Try It
我来试试
LC46 的自动算法会系统枚举所有分支;这里是让你体验 path / used / backtrack。
_
_
_
点击英雄,亲手体验 path / used / backtrack。
手动结果墙为空
Postmortem
游戏事故复盘
事故 1:忘记 path.pop()
后果:角色下不了场,path 越来越长。
事故 2:忘记 used[i] = false
后果:角色一直处于冷却状态,后面的排列会漏掉。
事故 3:res.push(path) 没复制
后果:结果墙上的卡片会被后续回溯污染,最后可能全变空。
事故 4:排列和组合混淆
后果:[1,2,3] 和 [1,3,2] 是两个答案,因为排列关心顺序。