排列问题 2021-10-28

46. 全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同

示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:
输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:
输入:nums = [1]
输出:[[1]]
解析过程:

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        //DFS,可以出现重复
        int len=nums.length;
        //使用一个动态数组保存所有可能的全排列
        List<List<Integer>> collect=new ArrayList<>();
        //path变量类似于一个栈,往下走一层的时候,path 变量在尾部追加,而往回走的时候,需要撤销上一次的选择,也是在尾部操作;path是根节点到叶子节点的路径
        List<Integer> path=new ArrayList<>();
        if(len==0){
            return collect;
        }
        //depth表示当前程序递归到第几层;selected 初始化的时候都为 false ,表示这些数还没有被选择,当选定一个数的时候,这个数组的相应位置会被设置为 true
        boolean[] selected=new boolean[len];
        //递归调用DFS
        DFS(nums,len,0,path,selected,collect);
        return collect;
    }
    public void DFS(int[] nums,int len,int depth,List<Integer> path,boolean[] selected,List<List<Integer>> collect){
        //递归终止条件:一个排列中的数字已经够了
        if(depth==len){
            collect.add(new ArrayList(path));
            return;
        }
        //若是非叶子节点,产生不同的分支,在还未选择的数中依次选择一个元素作为下一个位置的元素
        for(int i=0;i<len;i++){
            if(!selected[i]){
                //将该数加入路径中
                path.add(nums[i]);
                selected[i]=true;
                DFS(nums,len,depth+1,path,selected,collect);
                // 下面这两行代码发生 「回溯」,回溯发生在从深层结点回到浅层结点的过程
                selected[i]=false;
                path.remove(path.size()-1);
            }
        }
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38.3 MB, 在所有 Java 提交中击败了94.94%的用户
通过测试用例:26 / 26

47. 全排列 II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
提示:
1 <= nums.length <= 8
-10 <= nums[i] <= 10

示例 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]]
解析过程:

class Solution {
    //定义一个标记数组来标记已经填过的数
    boolean[] selected;
    public List<List<Integer>> permuteUnique(int[] nums) {
        //使用一个动态数组保存所有可能的全排列
        List<List<Integer>> collect =new ArrayList<List<Integer>>();
        //path:当前排列
        List<Integer> path=new ArrayList<Integer>();
        selected = new boolean[nums.length];
        Arrays.sort(nums);
        Backtrack(nums,0,path,collect);
        return collect;
    }
    public void Backtrack(int[] nums,int len,List<Integer> path,List<List<Integer>> collect){
        //len:下一个待填入的位置是第len个位置(下标从0开始)
        //如果已经填完了nums.length 个位置,找到了一个可行的解,就将 path 放入答案数组中
        if(len == nums.length){
            collect.add(new ArrayList<Integer>(path));
            return;
        }
        for(int i=0;i<nums.length;++i){
            //每次填入的数一定是这个数所在重复数集合中从左往右第一个未被填过的数字,这样就可以保证全排列不重复(提前将原数组进行排序,相同的数字就会相邻)
            if (selected[i] || (i > 0 && nums[i] == nums[i - 1] && !selected[i - 1])) {
                continue;
            }
            path.add(nums[i]);
            selected[i]=true;
            Backtrack(nums,len+1,path,collect);
            //搜索回溯的时候要撤销该个位置填的数以及标记,并继续尝试其他没被标记过的数
            selected[i]=false;
            path.remove(len);
        }
    }
}

结果:
执行用时:1 ms, 在所有 Java 提交中击败了99.31%的用户
内存消耗:39.1 MB, 在所有 Java 提交中击败了59.47%的用户
通过测试用例:33 / 33

869. 重新排序得到 2 的幂
给定正整数 N ,我们按任何顺序(包括原始顺序)将数字重新排序,注意其前导数字不能为零。
如果我们可以通过上述方式得到 2 的幂,返回 true;否则,返回 false。
提示:1 <= N <= 10^9

示例 1:
输入:1
输出:true

示例 2:
输入:10
输出:false

示例 3:
输入:16
输出:true

示例 4:
输入:24
输出:false

示例 5:
输入:46
输出:true
解析过程:

class Solution {
    boolean[] selected;
    public boolean reorderedPowerOf2(int n) {
        //将 n 的十进制表示视作一个字符数组
        char[] nums=Integer.toString(n).toCharArray(); 
        //然后枚举数组中的全排列,再分为两步,这两步分别是Leetcode中47题和231题
        Arrays.sort(nums);
        selected=new boolean[nums.length];
        return Backtrack(nums,0,0);
    }
    //1.找出可能包含重复字符且前导数字不能为零的数组的全排列
    public boolean Backtrack(char[] nums,int len,int element){
        //len:下一个待填入的位置是第len个位置(下标从0开始)
        //如果len=nums.length,则说明找到一个可行解,并判断该整数是不是2的幂
        if(len == nums.length){
            return isValid(element);
        }
        for(int i=0;i<nums.length;++i){
            //其前导数字不能为零,且每次填入的数一定是这个数所在重复数集合中从左往右第一个未被填过的数字,这样就可以保证全排列不重复(提前将原数组进行排序,相同的数字就会相邻)
            if((element == 0 && nums[i]=='0')||selected[i] || (i>0&& !selected[i-1] && nums[i]==nums[i-1]) ){
                continue;
            }
            selected[i]=true;
            //在递归搜索全排列的同时,计算出当前排列的已枚举的部分所对应的整数element。在枚举当前排列的下一个字符时,将下一个字符加到num的末尾,然后递归到下一层。
            if(Backtrack(nums,len+1,element*10+nums[i]-'0')){
                return true;
            }
            selected[i]=false;
        }
        return false;
    } 
    //2.判断排列所得到的整数是否为2的幂
    public boolean isValid(int n){
        return (n&(n-1))==0;
    }
}

结果:
执行用时:34 ms, 在所有 Java 提交中击败了21.99%的用户
内存消耗:35.2 MB, 在所有 Java 提交中击败了84.65%的用户
通过测试用例:136 / 136

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值