47.全排列Ⅱ

47.全排列Ⅱ

题目

给定一个可包含重复数字的序列 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]]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

输入的nums是可以包含重复数字的,对排列之后的结果要求是不重复的。

之前处理输入包含重复,输出不重复的思路是:对输入数组排序是为了方便剪枝掉重复的结果。

image

image

通过分别画出nums无序与有序的图,发现树的同一层仍然是不能重复取相同值的,那么还是需要对输入数组进行排序,通过比较前后取值是否一样来判断是否取值重复。

对于每一个分支path来说,因为每一次循环都是从nums[0]取到最后,那么已经取过的位置不能再取了。
46题我们使用的是path的路径之中有就说明已经取值了,没有就说明还没有取过值。但这道题nums是可以有值重复的元素,所以设置一个visited数组记录元素是否被访问过。

递归的参数和返回值

List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
void backtracing(int [] nums);

回溯的终止条件

全排序是对nums的元素进行排序,那么排序完path的长度应该等于nums的长度。

if(path.size() == nums.length){
	res.add(new ArrayList(path));
	return;
}

递归的单层逻辑

这里关于哪些值不可以我其实有点不清楚。重新把抽象树补充具体了。

image

同一分支的相同值情况
visited[i-1]==1&&nums[i]==nums[i-1]说明1#取值的时候1已经取了,那么当前情况是1#与1在同一分支上,这种情况是允许的。
同一层相同值的情况
visited[i]==0&&nums[i]==nums[i-1]说明当前分支1是没取的,1#与1是在同一层的情况,这种情况是不允许的。

for(int i=0;i<nums.length;i++){
     if(visited[i]==1) continue; //一个分支上的每个位置只能取1次
     if(i>0&&visited[i-1]==0&&nums[i]==nums[i-1])continue;
     visited[i]=1;
     path.add(nums[i]);
     backtracing(nums,visited);
     visited[i]=0;
     path.remove(path.size()-1);
   }

代码

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path;
    public List<List<Integer>> permuteUnique(int[] nums) {
        if(nums.length==0) return res;
        path = new ArrayList<>();
		//如果该数组只有的取值只有0或1 可以考虑用boolean[] visited = new boolean[nums.length];
        int [] visited = new int [nums.length];
        Arrays.sort(nums);
        backtracing(nums,visited);
        return res;
    }
    void backtracing(int[] nums,int[]visited){
        if(path.size() == nums.length){
        	res.add(new ArrayList(path));
	        return;
        }
        for(int i=0;i<nums.length;i++){
            if(visited[i]==1) continue;
            if(i>0&&visited[i-1]==0&&nums[i]==nums[i-1])continue;
            visited[i]=1;
            path.add(nums[i]);
            backtracing(nums,visited);
            visited[i]=0;
            path.remove(path.size()-1);
        }
    }
}

扩展

阅读了大佬的题解,补充一种新思路

对于排列问题,树层上去重和树枝上去重,都是可以的,但是树层上去重效率更高

树层上去重

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) { 
    continue;
}

image

数枝上去重

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == true) { 
    continue;
}

image

posted @ 2021-06-22 11:03  rananie  阅读(73)  评论(0编辑  收藏  举报