全排列 --- 回溯

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

你主要有这几个问题:


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
撤销时删除最后一个元素
相关推荐
科技大视界1 小时前
投资AI项目,传统尽调不够用了——李章虎律师拆解算法、数据、算力三大雷区
人工智能·算法·数据挖掘
郝学胜-神的一滴2 小时前
算法实战:最小k个数——大顶堆的优雅解法
开发语言·数据结构·c++·python·程序人生·算法·排序算法
Irissgwe2 小时前
算法滑动窗口
数据结构·算法
怪兽学LLM2 小时前
LeetCode 105. 从前序与中序遍历序列构造二叉树:分治递归思路详解
算法·leetcode·职场和发展
可编程芯片开发2 小时前
基于PI控制的三相整流器控制系统的simulink建模与仿真,包含超级电容充电和电机
算法
酿情师2 小时前
区块链共识算法深度拆解:PoW、PoS、PBFT、Raft 原理解析
算法·区块链·共识算法
hansang_IR2 小时前
【记录】「SCOI2016」三道模拟赛/26.7.12
c++·算法
退休倒计时3 小时前
【每日一题】LeetCode 39. 组合总和 TypeScript
算法·leetcode·typescript
不是az3 小时前
力控相关知识点
算法·机器学习·最小二乘法
战血石LoveYY3 小时前
Integer类型超限,导致数据异常
java·算法