2898 字
约 9 分钟
11
二哥的 LeetCode 刷题笔记:047.全排列 II
无标签

二哥的 LeetCode 刷题笔记:047.全排列 II

作者:沉默王二
发布时间:2022-10-10T11:13:37.746+0800


047.全排列 II

题意

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有 不重复 的全排列。

难度

中等

示例

示例 1:

输入:nums = [1,1,2]

[[1,1,2], [1,2,1], [2,1,1]]

示例 2:

输入:nums = [1,2,3]

分析

这题是 046.全排列 的延续,但是纵向对比一下,就能发现这题和 046.全排列 的关系,其实和 039.组合总和 与 040.组合总和II 两者的关系如出一辙。

如果说nums中没有重复元素的话,那么这道题就跟 046.全排列 一模一样,可惜有重复元素的存在,让我们不得不重新考虑。

首先,因为nums内部的顺序并不会影响答案的组成,所以我们不妨把它们从小到大排个序,让他们相同的归属到一起,方便我我们参考 040.组合总和II 的方法来处理重复的元素。

还是按照 046.全排列 的思路来挨个选择合适的排列,然后加入答案中。

来看这个例子:nums = [1,1,2] 如果按照 046.全排列 的思路的话,我们会得到这样子的结果:

ans = [

[1,2,1],[1,1,2],[1,2,1],[2,1,1],[2,1,1]]

因为这个思路会把两个一样的1强硬的加上序号,让它们有了 差别 ,但实际上它们并无差别,所以怎么来避开呢?

其实我们只要保证同个位置同个元素只被填入一次即可。

给出以下的一个判断条件:

if(vis[i] || (i > 0 && numsIn[i] == numsIn[i - 1] && !vis[i - 1]))

如果符合上述条件,直接跳过当前元素的枚举即可。

下面给出一个粗略证明上述条件的合法性:

首先如果当前nums[i]已经使用过了——也就是vis[i] == true的话,那么显然是不能够再次选择的。

如果当前元素与前一个元素重复了,而且前面一个元素还没有被使用的时候,我们不能使用当前元素,这样子其实是规定了每个重复元素的排列,让每个重复的元素只能按照唯一固定的顺序来加入list中,从而防止了本来是一样的元素,结果两个仅仅是调换顺序,则形成了新的排列(实际上并没有)。

class Solution {

    boolean[] vis;    int[] numsIn;
    private void DFS(List nowSta){        if(nowSta.size() == numsIn.length){            res.add(new ArrayList<>(nowSta));            return ;        }        for(int i = 0;i < numsIn.length;i++){            if(vis[i] || (i > 0 && numsIn[i] == numsIn[i - 1] && !vis[i - 1]))                continue;            nowSta.add(numsIn[i]);            vis[i] = true;            DFS(nowSta);            nowSta.remove(nowSta.size() - 1);            vis[i] = false;        }    }    public List<List> permuteUnique(int[] nums) {        numsIn = nums;        List ans = new ArrayList<>();        Arrays.sort(numsIn);        vis = new boolean[nums.length];        DFS(ans);        return res;    }}

时间复杂度:

时间复杂度其实和 046.全排列 是一样的,因为我们只是多了一个排序和一个规定重复元素顺序的操作,所以大体上并无差别。

总结

我们在做题的时候不仅仅要纵向对比( 046.全排列 和 047.全排列 II),也要横向对比 ( 040.组合总和 II 和 047.全排列 II),这样子才能够充分从过去的题解中汲取力量,从而更加容易的解决眼下的题目。

力扣链接:https://leetcode.cn/problems/permutations-ii/

一步一个脚印

不积跬步无以至千里,不积小流无以成江海。LeetCode - 100天从算法小白到卷王正式启动了,我们计划周一到周五至少每天更新一篇,周六周日更新一篇,目标 300 道 LeetCode 经典题。

PDF 版本目前只针对**二哥编程星球 的球友开放,如果你也想一起打卡学习算法的话,戳链接 **加入我们吧。

一个人可以走得很快,但一群人才能走得更远。二哥编程星球 (目前已有 890 多名球友加入)里面的每个球友都非常的友善,除了鼓励你,还会给你提出合理的建议,一起冲!

047.全排列 II

题意

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有 不重复 的全排列。

难度

中等

示例

示例 1:

输入:nums = [1,1,2]

[[1,1,2], [1,2,1], [2,1,1]]

示例 2:

输入:nums = [1,2,3]

分析

这题是 046.全排列 的延续,但是纵向对比一下,就能发现这题和 046.全排列 的关系,其实和 039.组合总和 与 040.组合总和II 两者的关系如出一辙。

如果说nums中没有重复元素的话,那么这道题就跟 046.全排列 一模一样,可惜有重复元素的存在,让我们不得不重新考虑。

首先,因为nums内部的顺序并不会影响答案的组成,所以我们不妨把它们从小到大排个序,让他们相同的归属到一起,方便我我们参考 040.组合总和II 的方法来处理重复的元素。

还是按照 046.全排列 的思路来挨个选择合适的排列,然后加入答案中。

来看这个例子:nums = [1,1,2] 如果按照 046.全排列 的思路的话,我们会得到这样子的结果:

ans = [

[1,2,1],[1,1,2],[1,2,1],[2,1,1],[2,1,1]]

因为这个思路会把两个一样的1强硬的加上序号,让它们有了 差别 ,但实际上它们并无差别,所以怎么来避开呢?

其实我们只要保证同个位置同个元素只被填入一次即可。

给出以下的一个判断条件:

if(vis[i] || (i > 0 && numsIn[i] == numsIn[i - 1] && !vis[i - 1]))

如果符合上述条件,直接跳过当前元素的枚举即可。

下面给出一个粗略证明上述条件的合法性:

首先如果当前nums[i]已经使用过了——也就是vis[i] == true的话,那么显然是不能够再次选择的。

如果当前元素与前一个元素重复了,而且前面一个元素还没有被使用的时候,我们不能使用当前元素,这样子其实是规定了每个重复元素的排列,让每个重复的元素只能按照唯一固定的顺序来加入list中,从而防止了本来是一样的元素,结果两个仅仅是调换顺序,则形成了新的排列(实际上并没有)。

class Solution {

    boolean[] vis;    int[] numsIn;
    private void DFS(List nowSta){        if(nowSta.size() == numsIn.length){            res.add(new ArrayList<>(nowSta));            return ;        }        for(int i = 0;i < numsIn.length;i++){            if(vis[i] || (i > 0 && numsIn[i] == numsIn[i - 1] && !vis[i - 1]))                continue;            nowSta.add(numsIn[i]);            vis[i] = true;            DFS(nowSta);            nowSta.remove(nowSta.size() - 1);            vis[i] = false;        }    }    public List<List> permuteUnique(int[] nums) {        numsIn = nums;        List ans = new ArrayList<>();        Arrays.sort(numsIn);        vis = new boolean[nums.length];        DFS(ans);        return res;    }}

时间复杂度:

时间复杂度其实和 046.全排列 是一样的,因为我们只是多了一个排序和一个规定重复元素顺序的操作,所以大体上并无差别。

总结

我们在做题的时候不仅仅要纵向对比( 046.全排列 和 047.全排列 II),也要横向对比 ( 040.组合总和 II 和 047.全排列 II),这样子才能够充分从过去的题解中汲取力量,从而更加容易的解决眼下的题目。

力扣链接:https://leetcode.cn/problems/permutations-ii/

一步一个脚印

不积跬步无以至千里,不积小流无以成江海。LeetCode - 100天从算法小白到卷王正式启动了,我们计划周一到周五至少每天更新一篇,周六周日更新一篇,目标 300 道 LeetCode 经典题。

PDF 版本目前只针对**二哥编程星球 的球友开放,如果你也想一起打卡学习算法的话,戳链接 **加入我们吧。

一个人可以走得很快,但一群人才能走得更远。二哥编程星球 (目前已有 890 多名球友加入)里面的每个球友都非常的友善,除了鼓励你,还会给你提出合理的建议,一起冲!

047.全排列 II

题意

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有 不重复 的全排列。

难度

中等

示例

示例 1:

输入:nums = [1,1,2]

[[1,1,2], [1,2,1], [2,1,1]]

示例 2:

输入:nums = [1,2,3]

分析

这题是 046.全排列 的延续,但是纵向对比一下,就能发现这题和 046.全排列 的关系,其实和 039.组合总和 与 040.组合总和II 两者的关系如出一辙。

如果说nums中没有重复元素的话,那么这道题就跟 046.全排列 一模一样,可惜有重复元素的存在,让我们不得不重新考虑。

首先,因为nums内部的顺序并不会影响答案的组成,所以我们不妨把它们从小到大排个序,让他们相同的归属到一起,方便我我们参考 040.组合总和II 的方法来处理重复的元素。

还是按照 046.全排列 的思路来挨个选择合适的排列,然后加入答案中。

来看这个例子:nums = [1,1,2] 如果按照 046.全排列 的思路的话,我们会得到这样子的结果:

ans = [

[1,2,1],[1,1,2],[1,2,1],[2,1,1],[2,1,1]]

因为这个思路会把两个一样的1强硬的加上序号,让它们有了 差别 ,但实际上它们并无差别,所以怎么来避开呢?

其实我们只要保证同个位置同个元素只被填入一次即可。

给出以下的一个判断条件:

if(vis[i] || (i > 0 && numsIn[i] == numsIn[i - 1] && !vis[i - 1]))

如果符合上述条件,直接跳过当前元素的枚举即可。

下面给出一个粗略证明上述条件的合法性:

首先如果当前nums[i]已经使用过了——也就是vis[i] == true的话,那么显然是不能够再次选择的。

如果当前元素与前一个元素重复了,而且前面一个元素还没有被使用的时候,我们不能使用当前元素,这样子其实是规定了每个重复元素的排列,让每个重复的元素只能按照唯一固定的顺序来加入list中,从而防止了本来是一样的元素,结果两个仅仅是调换顺序,则形成了新的排列(实际上并没有)。

class Solution {

    boolean[] vis;    int[] numsIn;
    private void DFS(List nowSta){        if(nowSta.size() == numsIn.length){            res.add(new ArrayList<>(nowSta));            return ;        }        for(int i = 0;i < numsIn.length;i++){            if(vis[i] || (i > 0 && numsIn[i] == numsIn[i - 1] && !vis[i - 1]))                continue;            nowSta.add(numsIn[i]);            vis[i] = true;            DFS(nowSta);            nowSta.remove(nowSta.size() - 1);            vis[i] = false;        }    }    public List<List> permuteUnique(int[] nums) {        numsIn = nums;        List ans = new ArrayList<>();        Arrays.sort(numsIn);        vis = new boolean[nums.length];        DFS(ans);        return res;    }}

时间复杂度:

时间复杂度其实和 046.全排列 是一样的,因为我们只是多了一个排序和一个规定重复元素顺序的操作,所以大体上并无差别。

总结

我们在做题的时候不仅仅要纵向对比( 046.全排列 和 047.全排列 II),也要横向对比 ( 040.组合总和 II 和 047.全排列 II),这样子才能够充分从过去的题解中汲取力量,从而更加容易的解决眼下的题目。

力扣链接:https://leetcode.cn/problems/permutations-ii/

一步一个脚印

不积跬步无以至千里,不积小流无以成江海。LeetCode - 100天从算法小白到卷王正式启动了,我们计划周一到周五至少每天更新一篇,周六周日更新一篇,目标 300 道 LeetCode 经典题。

PDF 版本目前只针对**二哥编程星球 的球友开放,如果你也想一起打卡学习算法的话,戳链接 **加入我们吧。

一个人可以走得很快,但一群人才能走得更远。二哥编程星球 (目前已有 890 多名球友加入)里面的每个球友都非常的友善,除了鼓励你,还会给你提出合理的建议,一起冲!

            知识星球
            
    
    
        
        扫码加入星球
        查看更多优质内容
    
    https://wx.zsxq.com/mweb/views/joingroup/join_group.html?group_id=15522885221412
二哥的 LeetCode 刷题笔记:047.全排列 II
https://clxhxhhr.top/posts/2511/
作者
clxstart
发布于
2026-09-19
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。