全排列 --- 回溯

你这道「全排列」的问题,不是不会思路,而是回溯代码的顺序和细节容易写乱

你主要有这几个问题:


1. 结束条件位置放错了

你把:

复制代码
复制代码
if (result.size() == n) {
    list.add(result);
    return;
}

写在了 for 循环里面。

但正确习惯是:递归函数一进来,先判断是否已经完成。

应该写在 for 前面:

复制代码
复制代码
if (result.size() == nums.length) {
    list.add(new ArrayList<>(result));
    return;
}

原因是:

每次进入 dfs,都先判断当前路径是不是已经凑够长度了。

凑够了就保存答案,不需要再进入 for 循环。


2. 没有复制 result

你写的是:

复制代码
复制代码
list.add(result);

这是错的。

因为 result 是一个会不断变化的列表,后面还会添加、删除。

正确写法:

复制代码
复制代码
list.add(new ArrayList<>(result));

意思是:

把当前 result 复制一份保存起来。

否则最后答案可能全都变成空数组,或者内容混乱。


3. 递归调用的位置不严谨

你原来写的是:

复制代码
复制代码
if (!used[i]) {
    result.add(nums[i]);
    used[i] = true;
}

dfs(nums);

result.remove(nums[i]);
used[i] = false;

问题是:

即使 used[i] == true,没有选择这个数,你还是会执行:

复制代码
复制代码
dfs(nums);

这会导致逻辑混乱。

正确写法是:

复制代码
复制代码
if (used[i]) {
    continue;
}

result.add(nums[i]);
used[i] = true;

dfs(nums);

result.remove(result.size() - 1);
used[i] = false;

也就是说:

用过的数字直接跳过。

没用过的数字,才执行:选择 → 递归 → 撤销。


4. 删除元素写错了

你写的是:

复制代码
复制代码
result.remove(nums[i]);

这个很容易出错。

因为在 Java 的 List<Integer> 里:

复制代码
复制代码
remove(数字)

会优先理解成:

删除这个下标位置的元素

而不是删除这个值。

比如:

复制代码
复制代码
result.remove(2);

它的意思可能是:

删除下标为 2 的元素

不是删除数字 2

正确写法:

复制代码
复制代码
result.remove(result.size() - 1);

因为回溯时,我们一定是删除刚刚加入的最后一个数字


5. 回溯模板顺序还不熟

你现在最大的问题是:知道要递归,但不知道代码应该按什么顺序摆。

全排列的固定顺序是:

复制代码
复制代码
1. 判断是否完成
2. for 循环枚举每个数字
3. 如果用过,跳过
4. 做选择
5. 递归
6. 撤销选择

对应代码:

复制代码
复制代码
public void dfs(int[] nums) {
    if (result.size() == nums.length) {
        list.add(new ArrayList<>(result));
        return;
    }

    for (int i = 0; i < nums.length; i++) {
        if (used[i]) {
            continue;
        }

        result.add(nums[i]);
        used[i] = true;

        dfs(nums);

        result.remove(result.size() - 1);
        used[i] = false;
    }
}

一句话总结你的问题

你已经知道要用:

复制代码
复制代码
result
used
dfs

说明大方向是对的。

你卡住的是:

回溯不是随便递归,而是必须严格按照

判断结束 → 枚举选择 → 做选择 → 递归 → 撤销选择

这个顺序写。

以后你写全排列,先默写这个模板:

复制代码
复制代码
if (路径长度够了) {
    保存路径的拷贝;
    return;
}

for (每一个选择) {
    if (这个选择不能用) {
        continue;
    }

    做选择;
    dfs();
    撤销选择;
}

你这次的核心错误可以记成四句话:

复制代码
复制代码
结束条件放 for 前面
保存答案要 new ArrayList
用过的数字直接 continue
撤销时删除最后一个元素
相关推荐
玉树临风ives1 小时前
atcoder ABC 460 题解
数据结构·c++·算法
水无痕simon1 小时前
9 C语言的基础练习
c语言·开发语言·算法
8Qi81 小时前
LeetCode 124. 二叉树中的最大路径和(Hard)
算法·leetcode·二叉树·递归
And_Ii1 小时前
LeetCode 1. 两数之和 python
数据结构·算法·leetcode
東隅已逝,桑榆非晚1 小时前
C语言预处理详解:从宏到条件编译
c语言·笔记·算法
cpp_25011 小时前
P10377 [GESP202403 六级] 好斗的牛
数据结构·c++·算法·题解·洛谷·gesp六级
邪修king1 小时前
C++ 红黑树自平衡核心:旋转变色、规则详解与 STL 选型逻辑
数据结构·c++·b树·算法
随意起个昵称3 小时前
线性dp-计数类题目10(ZBRKA)
算法·动态规划
Navigator_Z9 小时前
LeetCode //C - 1089. Duplicate Zeros
c语言·算法·leetcode