力扣刷题笔记【回溯】 → 47. 全排列 II

185 阅读1分钟

这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战

题目

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

示例

输入: nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]
输入: 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

解题思路

回溯

对于全排列的题来说,大多数的解法都是采用回溯算法来实现的。我们需要从左到右依次将数组中的元素与其余元素进行组合,这种类似穷举的操作,可以采用【回溯】来模拟该过程。

同时题目要求是返回所有不重复的排列,那么我们还需要在原有的基础上做一个剪枝操作,将重复的元素剔除掉,以保证结果的唯一性。

class Solution {
    private List<List<Integer>> list;
    private boolean[] vis;
    public List<List<Integer>> permuteUnique(int[] nums) {
        int len = nums.length;
        list = new ArrayList<>();
        if(0 == len){
            return list;
        }
        vis = new boolean[len];
        
        // 这里需要先进行一个排序操作,便于后面进行剪枝判断
        Arrays.sort(nums);
        dfs(nums, 0, len, new LinkedList<Integer>());
        return list;
    }

    public void dfs(int[] nums, int index, int n, LinkedList<Integer> numList){
        // 添加新的排列并结束
        if(index == n){
            list.add(new ArrayList(numList));
            return;
        }
        
        for(int i = 0; i < n; ++i){
            // 剪枝判断,vis[i]为true表示已经遍历过该元素,则跳过当前循环
            // 或者当前元素与前面元素一致,表示以遍历过该组合,也跳过循环
            if(vis[i] || (i > 0 && nums[i] == nums[i - 1] && !vis[i - 1])){
                continue;
            }
            vis[i] = true;
            // 添加元素
            numList.add(nums[i]);
            // 向下延伸
            dfs(nums, index + 1, n, numList);
            // 回溯
            numList.removeLast();
            vis[i] = false;
        }
    }
}

 复杂度分析

  •   时间复杂度:O(NN!)O(N * N!)
  •   空间复杂度:O(N)O(N)

最后

文章有写的不好的地方,请大佬们不吝赐教,错误是最能让人成长的,愿我与大佬间的距离逐渐缩短!

如果觉得文章对你有帮助,请 点赞、收藏、关注、评论 一键四连支持,你的支持就是我创作最大的动力!!!

题目出处: leetcode-cn.com/problems/pe…