[ 热题 HOT 100]---46. 全排列➕47. 全排列 II--- 回溯法/dfs

1 题目描述

    1. 全排列
      给定一个 没有重复 数字的序列,返回其所有可能的全排列。
    1. 全排列 II
      给定一个可包含重复数字的序列,返回所有不重复的全排列。

2 解题思路

    1. 全排列
  • 解决方法: 回溯法/dfs
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    1. 全排列 II

这一题是在「力扣」第 46 题: “全排列” 的基础上增加了“序列中的元素可重复”这一条件,但要求返回的结果又不能有重复元素。

如果要比较两个列表是否一样,一个很显然的办法是分别排序,然后逐个比对既然要排序,我们可以在搜索之前就对候选数组排序,一旦发现这一支搜索下去可能搜索到重复的元素就停止搜索,这样结果集中不会包含重复元素

在这里插入图片描述

在这里插入图片描述

这个题目相比于46题,要多做的工作就是去重

可以先排序,这个方便后面剪枝操作,所以要增加排序的代码

然后就是判断剪枝的条件,if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1])
这里,i>0 是为了为了保证 nums[i - 1] 有意义,
nums[i] == nums[i - 1],就是说该元素与前一个元素相等,这就是剪枝的关键,要被剪掉
写 !used[i - 1] 是因为 nums[i - 1] 在深度优先遍历的过程中刚刚被撤销选择

还有就是循环题的第一个判断条件,
在46题中,是if(!used[i]),但是在47题中,是if(used[i])
这也是46 和 47题的区别~~~
新加的代码,如果已经被使用,那么继续下一步continue

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/permutations-ii/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liwe-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

3 解决代码

    1. 全排列
  • 解决方法: 回溯法/dfs《Java代码》
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        int len = nums.length;
        List<List<Integer>>  res = new ArrayList<>();
        //特例:如果为空,返回的是空数组
        if(len == 0){
            return res;
        }
        //used布尔类型的数组,表示有没有使用过
        //当检查元素是否使用的时候时间复杂度为1, 用空间换取时间
        boolean[] used = new boolean[len];
        List<Integer> path = new ArrayList<>();
        dfs(nums, len, 0, path, used, res);
        return res;
    }

    public void dfs(int[] nums, int len, int depth, List<Integer> path, boolean[] used, List<List<Integer>> res){
        //已经找到满足长度的数组,可以进行返回
        if(depth == len){
            //这个地方要进行数据的复制,否则返回的是空
            res.add(new ArrayList<>(path));
            return;
        }
        //循环实现在,还未选择的数中依次选择一个元素作为下一个位置的元素
        for(int i = 0; i < len; i++){
            if(!used[i]){
                //没有被用过加入到path路径之中,把used状态设为true表示已使用
                path.add(nums[i]);
                used[i] = true;
                //递归
                dfs(nums, len, depth + 1, path, used,res);
                //这一步就是完成状态重制工作,从深层次节点回到浅层次节点
                //use的状态设为false,表示没有使用,并且将最后加入的那个元素移除
                used[i] = false;
                path.remove(path.size() - 1);
            }
        }
    }
}
  • 解决方法: 回溯法/dfs《python3代码》
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        def dfs(nums, size, depth, path, used, res):
            if depth == size:
                #这个地方实现的也是元素的复制
                res.append(path[:])
                return 
            for i in range(size):
                if not used[i]:
                    used[i] = True
                    path.append(nums[i])
                    dfs(nums, size, depth + 1, path, used, res)
                    used[i] = False
                    path.pop()
        size = len(nums)
        if len(nums) == 0:
            return []
        #布尔类型的数组used
        used = [False for _ in range(size)]
        res = []
        # path = []
        dfs(nums, size, 0, [], used,res)
        return res
    1. 全排列II
  • 解决方法: 回溯法/dfs《Java代码》
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);
        //used布尔类型的数组,表示有没有使用过
        //当检查元素是否使用的时候时间复杂度为1, 用空间换取时间
        boolean[] used = new boolean[len];
        List<Integer> path = new ArrayList<>();
        dfs(nums, len, 0, path, used, res);
        return res;
    }

    public void dfs(int[] nums, int len, int depth, List<Integer> path, boolean[] used, 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路径之中,把used状态设为true表示已使用
            used[i] = true;
            path.add(nums[i]);
            //递归
            dfs(nums, len, depth + 1, path, used,res);
            //这一步就是完成状态重制工作,从深层次节点回到浅层次节点
            //use的状态设为false,表示没有使用,并且将最后加入的那个元素移除
            used[i] = false;
            path.remove(path.size() - 1);    
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值