Version: Next
46.全排列
难度 中等
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
DFS
按 DFS 套路来即可
- 在枚举每层所有的选择时,注意本题中,元素是不可重复的,也就是之前被选过的数字,之后的层就不能再选了
如何实现不重复
- 方法一:设置一个 boolean 数组,与元素数目一致,标识对应元素有没有使用过
- 但是:在回溯时,必须恢复现场,因为选择元素发生了退回,则 boolean 数组的值也要同步退回
方法一 使用布尔数组public class _46全排列 {public List<List<Integer>> permute(int[] nums) {Integer[] singleResult = new Integer[nums.length];List<List<Integer>> resultList = new ArrayList<>();boolean[] isNumUsed = new boolean[nums.length];if (nums.length == 1) {resultList.add(Collections.singletonList(nums[0]));return resultList;}dfs(0, nums, resultList, singleResult, isNumUsed);return resultList;}private void dfs(int depth, int[] nums, List<List<Integer>> resultList, Integer[] singleResult, boolean[] isNumUsed) {if (depth == nums.length) {// 找到了一个解,添加到结果集resultList.add(new ArrayList<>(Arrays.asList(singleResult)));return;}// 枚举当前层所有的选择for (int i = 0; i < nums.length; i++) {if (isNumUsed[i]) continue;singleResult[depth] = nums[i];isNumUsed[i] = true;dfs(depth + 1, nums, resultList, singleResult, isNumUsed);isNumUsed[i] = false; // 回溯回复现场}}public static void main(String[] args) {int[] nums = {1, 2, 3};_46全排列 obj = new _46全排列();List<List<Integer>> permute = obj.permute(nums);for (List<Integer> integers : permute) {for (Integer integer : integers)System.out.print(integer + ", ");System.out.print("\n");}}}
- 方法二:使用集合类 contains 过滤
使用集合简化public List<List<Integer>> permute(int[] nums) {List<Integer> singleResult = new ArrayList<>();List<List<Integer>> resultList = new ArrayList<>();if (nums.length == 1) {resultList.add(Collections.singletonList(nums[0]));return resultList;}dfs(0, nums, resultList, singleResult);return resultList;}private void dfs(int depth, int[] nums, List<List<Integer>> resultList, List<Integer> singleResult) {if (depth == nums.length) {// 找到了一个解,添加到结果集resultList.add(new ArrayList<>(singleResult));return;}// 枚举当前层所有的选择for (int num : nums) {if (singleResult.contains(num)) continue;singleResult.add(num);dfs(depth + 1, nums, resultList, singleResult);singleResult.remove(singleResult.size() - 1); // 回溯回复现场}}
- 但是,contains 方法使得时间复杂度变高
DFS 最佳实现
对于输入 [1, 2, 3]
- 在第 0 层,让 下标0 分别与 下标 0、1、2 交换
- 在第 1 层,让下标1 分别与 下标 1、2 交换
- .. 以此类推
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> resultList = new ArrayList<>();
if (nums.length == 1) {
resultList.add(Collections.singletonList(nums[0]));
return resultList;
}
dfs(0, nums, resultList);
return resultList;
}
private void dfs(int depth, int[] nums, List<List<Integer>> resultList) {
if (depth == nums.length) {
// 找到了一个解,添加到结果集
List<Integer> singleResList = new ArrayList<>();
for (int n : nums)
singleResList.add(n);
resultList.add(singleResList);
return;
}
// 枚举当前层所有的选择
// 从当前层 对应数值的下标开始遍历 , 因为之前的位置已经选定了
for (int i = depth; i < nums.length; i++) {
swap(nums, depth, i);
dfs(depth + 1, nums, resultList);
swap(nums, depth, i); // 回溯 恢复现场
}
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}