全排列 --- 回溯

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

你主要有这几个问题:


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
撤销时删除最后一个元素
相关推荐
鱼鱼不愚与3 小时前
《原来如此 | 第01期:为什么导航软件能预测红绿灯倒计时?》
算法
复杂网络8 小时前
论最小 Agent 计算机的形态
算法
kisshyshy1 天前
🍦 雪糕、食堂、火车厢:三幅漫画吃透栈、队列与链表
javascript·算法
猿人谷1 天前
不只是 CPU 阈值:STAR 如何用 GAT + Transformer 做容器级自动扩缩容?
人工智能·算法
复杂网络1 天前
Stable Diffusion 视觉大模型微调技术深度调研
算法
复杂网络1 天前
基于 Stable Diffusion 架构的视觉大模型代表性工作与原理深度解析
算法
MrZhao4001 天前
Agent Loop 如何用 Hook 扩展:权限、日志与工具拦截
算法
MrZhao4001 天前
Agent 为什么需要 Skills:别把所有知识都塞进 system prompt
算法
JieE2123 天前
LeetCode 101. 对称二叉树|JS 递归 + 迭代双解法,彻底搞懂镜像判断
javascript·算法
JieE2124 天前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试