DFS算法系列题 – 全排列II
DFS精选题- > 这次我们挑战的对象是:
全排列II
题目链接:47. 全排列 II - 力扣(LeetCode)
这道题和我们之前做的全排列不同的点在于这道题的题目包含了重复的数字
,要求我们返回不重复的全排列:
对此,我们需要引入一个算法策略来帮助我们解决重复数字的问题,那就是剪枝:
剪枝:其实就是通过特定的条件将我们不需要的搜索路径去掉,从而提高我们的代码运行效率,这就类似于剪掉搜索树中无用的枝条
那么,上述这道题的需要满足的剪枝条件又是什么呢?
在于以下两点:
- 同一个节点的所有的分支中,相同的数只能出现一次
- 同一个元素只能出现一次
通过上述条件,我们可以画出对应的决策树来观察情况:
通过对上述决策树的分析,我们需要创建一些特殊的变量来满足我们的需求:
boolean[] check
: 用来检测当前元素是否在之前层已经被使用过了List<Integer> path
: 用来记录路径- 我们通过
int[] nums
数组来存储元素,并且若当前层nums[i] == nums[i - 1]
,则可通过check[i - 1] == true
来判断当前数字是否是当前层第一个使用的重复数字,若上一层使用了重复数字元素i - 1, 则check[i - 1] == true,此时当前层则未有对该重复数字的使用,在当前层就能使用重复数字i了 - 到了新的一层会重新开始判断第一个重复的数字
- 在进行上述重复数字判断前,我们需要将传递进来的数值列表进行排序,使得出现的重复数字前后挨在一起,方便使用i, i - 1判断
分析完后,接下来我们开始编写代码!
代码示例
class Solution {
List<List<Integer>> ret;
boolean[] check;
public List<List<Integer>> permuteUnique(int[] nums) {
ret = new ArrayList<>();
check = new boolean[nums.length];
Arrays.sort(nums); // 这里记得将数组按顺序排列
dfs(nums, new ArrayList<>());
return ret;
}
public void dfs(int[] nums, List<Integer> path) {
if (path.size() == nums.length) {
ret.add(new ArrayList<>(path));
return;
}
for (int i = 0;i < nums.length;i++) {
if (!check[i] && (i == 0 || nums[i] != nums[i - 1] || check[i - 1] == true)) {
// 这里有个隐藏的条件,当nums[i] != nums[i-1] 时,由于是或判断,则能进行到check[i - 1] == true 判断则代表着num[i] == nums[i - 1]了
path.add(nums[i]);
check[i] = true;
dfs(nums, path);
path.remove(path.size() - 1);
check[i] = false;
}
}
}
}
这样这道题就解决了!总的来说这道题需要通过剪枝来找到解决重复问题的条件,并根据条件画出决策树进而编写dfs函数体!