全排列 II
- leetcode47. 全排列 II
- 题目描述
- 解题思路
- 代码演示
- 回溯算法专题
leetcode47. 全排列 II
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/permutations-ii
题目描述
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
提示:
1 <= nums.length <= 8
-10 <= nums[i] <= 10
解题思路
排列问题本身就是让你穷举元素的位置
但这个问题里,会有重复数字,因此我们要进行剪枝.
比如输入 nums = [2,2’,2’‘],产生的回溯树如下:,2’,2’’ 只是为了区分三个2.
他的全排列就是下图:
上面虽然有六个结果,但其实五个是重复的,实际只有一种情况.
我们要在排列时,对相同的情况进行剪枝.
如下图:
我们要把红色部分全部剪掉,因此就要有个标记,在没有重复的情况下,进行剪枝.
本题也是回溯算法的框架来做,只是要做好剪枝的问题.
再看一眼回溯算法的框架:
result = []
void process(选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
代码演示
class Solution {
//记录答案
List<List<Integer>> ans = new ArrayList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
process(nums,0);
return ans;
}
/**
* 回溯算法
*/
public void process(int[]nums,int index){
if(index == nums.length){
ans.add(toList(nums));
return ;
}
//用来标记相同的元素,
HashMap<Integer,Boolean> flag = new HashMap<>();
for(int i = index;i < nums.length;i++){
//前面没有做出过选择,才进行选择
if(!flag.containsKey(nums[i])){
//已经做了选择,就加到标记里
flag.put(nums[i],true);
//全排列是对元素出现位置的穷举,因此我们把他交换到别的位置上,继续进行递归,
//来让他去出现在不同的位置上
//这一步就是做选择,
swap(nums,i,index);
process(nums,index + 1);
//撤销选择
swap(nums,i,index);
}
}
}
/**
* 交换的方法
*/
public void swap(int[]nums,int i,int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
/**
* 数组转集合
*/
public List<Integer> toList(int[]nums){
List<Integer> ans = new ArrayList<>();
for(int a : nums){
ans.add(a);
}
return ans;
}
}
回溯算法专题
leetcode46. 全排列
leetcode39. 组合总和
leetcode216. 组合总和 III
leetcode90. 子集 II
leetcode40. 组合总和 II
leetcode77. 组合
leetcode78 子集