好问题!让我详细解释"用start避免重复"的含义。
组合问题:为什么需要start?
组合的定义 :选出k个数,顺序无关
-
1,2\] 和 \[2,1\] 是**同一个组合**
c
// ❌ 错误:没有start限制
void dfs(int n, int k, int *path, int pathSize, ...) {
if (pathSize == k) {
保存path;
return;
}
for (int i = 1; i <= n; i++) { // 每次都从1开始
if (!visited[i]) {
path[pathSize] = i;
dfs(n, k, path, pathSize+1, ...);
}
}
}
// n=4, k=2
// 会生成:[1,2], [2,1], [1,3], [3,1], ... 重复!
✅ 用start避免重复
c
void dfs(int n, int k, int start, int *path, int pathSize, ...) {
if (pathSize == k) {
保存path;
return;
}
for (int i = start; i <= n; i++) { // 从start开始,不回头
path[pathSize] = i;
dfs(n, k, i + 1, path, pathSize+1, ...); // 下次从i+1开始
}
}
关键 :i + 1 保证后面的数字永远比前面大
图解说明
n=4, k=2,用start:
DFS树(用start):
[]
/ | | \
1 2 3 4
/|\ |\ |
2 3 4 3 4 4
生成:
[1,2], [1,3], [1,4] ← 从1开始,后面只选2,3,4
[2,3], [2,4] ← 从2开始,后面只选3,4
[3,4] ← 从3开始,后面只选4
✅ 无重复:[2,1]不会出现,因为选了2后,start=3,不会回头选1
没有start:
DFS树(没有start):
[]
/ / | \ \
1 2 3 4
/ |\ |\ /|\ /|\
1 234 12 123 123...
生成:
[1,2], [1,3], [1,4]
[2,1], [2,3], [2,4] ← ❌ [2,1]是重复的
[3,1], [3,2], [3,4] ← ❌ [3,1],[3,2]是重复的
...
总结
start的作用:
- 保证每次只从当前数字后面选
- 避免选到前面的数字
- 自然地保证:
path[0] < path[1] < path[2] < ... - 因此 [1,2] 会生成,但 [2,1] 不会生成
记忆:
组合:顺序无关 → 用start保证递增 → 避免重复
排列:顺序相关 → 用visited避免重用 → 允许任意顺序
这就是为什么组合用start,而全排列用visited!