leetcode 46 全排列(47带重复元素)
当给定的数组没有重复元素时
方法一:运用临时的path路径,然后一个标记是否访问的visit[]数组,来进行DFS
注意java中的一个bug,一个List加到另外一个List里面,前者改变的时候,后者也会改变
方法二: 利用交换的思想,每次都固定前面排好的一部分,直接在给定的数组上面操作,从而省去visit数组和临时path路径
class Solution {
public List<List<Integer>> permute(int[] nums) {
int len = nums.length;
List<Integer> nnums = new ArrayList<>();
for(int i:nums){
nnums.add(i);
}
List<List<Integer>> res = new ArrayList<>();
dfs(nnums,len,0,res);
return res;
}
public void dfs(List<Integer> nums,int len,int index,
List<List<Integer>> res){
if(index==len){
res.add(new ArrayList(nums));
return;
}
for(int i=index;i<len;i++){
Collections.swap(nums,i,index);
dfs(nums,len,index+1,res);
Collections.swap(nums,i,index);
}
}
}
当给定的数组有重复元素时,需要进行剪枝(难点)
- 在图中 ② 处,搜索的数也和上一次一样,但是上一次的 1 还在使用中;
- 在图中 ① 处,搜索的数也和上一次一样,但是上一次的 1 刚刚被撤销,正是因为刚被撤销,下面的搜索中还会使用到,因此会产生重复,剪掉的就应该是这样的分支。
方法一思路图:
如何理解:
注意方法一需要排序
方法一:还是利用临时path路径和visit[]数组,加以判断
在代码中
- 当一个点访问了时候,就continue
- 当一个点满足上述的条件(与上一次搜索的数一样且上一个数没有在被使用)
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;
public class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
int len = nums.length;
List<List<Integer>> res = new ArrayList<>();
if (len == 0) {
return res;
}
// 排序(升序或者降序都可以),排序是剪枝的前提
Arrays.sort(nums);
boolean[] used = new boolean[len];
// 使用 Deque 是 Java 官方 Stack 类的建议
Deque<Integer> path = new ArrayDeque<>(len);
dfs(nums, len, 0, used, path, res);
return res;
}
private void dfs(int[] nums, int len, int depth, boolean[] used, Deque<Integer> path, List<List<Integer>> res) {
if (depth == len) {
res.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < len; ++i) {
if (used[i]) {
continue;
}
// 剪枝条件:i > 0 是为了保证 nums[i - 1] 有意义
// 写 !used[i - 1] 是因为 nums[i - 1] 在深度优先遍历的过程中刚刚被撤销选择
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
continue;
}
path.addLast(nums[i]);
used[i] = true;
dfs(nums, len, depth + 1, used, path, res);
// 回溯部分的代码,和 dfs 之前的代码是对称的
used[i] = false;
path.removeLast();
}
}
public static void main(String[] args) {
Solution solution = new Solution();
int[] nums = {1, 1, 2};
List<List<Integer>> res = solution.permuteUnique(nums);
System.out.println(res);
}
}
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/permutations-ii/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liwe-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
写 used[i - 1] 代码正确,但是不推荐的原因。https://leetcode-cn.com/problems/permutations-ii/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liwe-2/
方法二:在原数组上动刀
//别人的解法
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> output = new LinkedList<>();
ArrayList<Integer> nums_list = new ArrayList<>();
for (int num:nums){
nums_list.add(num);
}
int n = nums.length;
backTrack(n, nums_list, output, 0);
return output;
}
private void backTrack(int n, ArrayList<Integer> nums, List<List<Integer>> output, int first){
if (first == n){
output.add(new ArrayList<>(nums));
return;
}
for (int i=first; i<n; i++){
if (!isRepeat(nums, first, i)){
Collections.swap(nums, first, i);
backTrack(n, nums, output, first+1);
Collections.swap(nums, first, i);
}
}
}
private boolean isRepeat(ArrayList<Integer> nums, int first, int n){
int temp = nums.get(n);
for (int i=first; i<n; i++){
if (nums.get(i) == temp){
return true;
}
}
return false;
}
作者:cllh1999
链接:https://leetcode-cn.com/problems/permutations-ii/solution/gai-jin-46guan-fang-ti-jie-qu-zhong-fu-by-cllh1999/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
思路:
哪一位可以与后面不相同的换(这个reapeat函数在剑指offer38中可用Hashset实现),就换,形成子路,换了之后就锁住
种一棵树最好的时间是十年前,其次是现在。