面试热题(全排列)

        给定一个不含重复数字的整数数组 nums ,返回其 所有可能的全排列 。可以 按任意顺序 返回答案。

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

先在这里说明一下排列和组合的区别?

组合:是指从一个元素集合中选择出若干个元素,形成一个无序的子集,组合不考虑元素的顺序,只关注元素的选择

排列:是指从一个元素集合中选择出若干元素,形成一个有序的序列。排列关注元素的顺序。

简单的来说,就是排列是元素是有序的,组合是无序的

一般排列组合问题我们都可以看成是一棵树(每个元素不允许重复)           

因为我们这题要求的是不重复的排列数,所以我们的模板就可以套了(模板必须要记的——理解)

//不含重复元素的排列数
void backTrack(int[] nums){1
    for(int i=0;i<nums.length;i++){
        if(uesd[i])continue;
        used[i]=true;
        path.addLast(nums[i]);
        backTrack(nums);
        path.removeLast(nums[i]);
        used[i]=false;
    }

源代码如下:

    //存储结果集
    List<List<Integer>> list = new ArrayList<>();
    //路径
    Deque<Integer> path = new LinkedList<>();
    //是否被访问
    boolean[] visited = null;

    public List<List<Integer>> permute(int[] nums) {
        //对入参进行判断
        if (nums == null || nums.length == 0) {
            return list;
        }
        //对数组进行初始化
        visited=new boolean[nums.length];
        //开始递归,因为是排列,后面的元素也有可能在前面的元素前面,所以不需要传递索引
        backtracking(nums);
        //返回结果集
        return list;
    }

    private void backtracking(int[] nums) {
        //找到满足条件得到一种情况,存入结果集中
        if (path.size()== nums.length) {
            list.add(new ArrayList<>(path));
            return;
        }
        //遍历每一个元素
        for (int j = 0; j < nums.length; j++) {
            //如果被访问过,直接跳过,避免重复选择
            if(visited[j]){
                continue;
            }
            path.add(nums[j]);
            visited[j]=true;
            backtracking(nums);
            //回溯
            path.removeLast();
            visited[j]=false;
        }
}

在这里给大家提供我刷组合排列问题总结的模板:

组合子集问题每个元素的相对位置已经固定,所以每次去枚举的时候都是从自身的右侧开始枚举

排列问题的每个元素的相对位置是不固定的。左侧的元素可能会出现在右侧,故每次每次枚举都是从0位置上开始枚举的

  • 元素无重不可复选(nums中的元素唯一,每个元素最多只能被使用一次)

/*组合/子集问题回溯模板*/
/* [1,2,3]  */
void backTrack(int[] nums,int start){
    //顺序无关,每次从自身的右边开始
    for(int i=start;i<nums.length;i++){
        path.addLast(nums[i]);
        backTrack(nums,i+1);
        path.removeLast(nums[i]);
    }
}
/* 排列问题回溯模板*/
void backTrack(int[] nums){
     //顺序有关,每次从0开始
    for(int i=0;i<nums.length;i++){
        if(uesd[i])continue;
        used[i]=true;
        path.addLast(nums[i]);
        backTrack(nums);
        path.removeLast(nums[i]);
        used[i]=false;
    }
}
  • .元素可重不可复选(nums中的元素可以存在重复,每个元素最多只能被使用一次)
    Arrays.sort(nums);
    /* 组合/子集问题回溯算法框架 */
    void backtrack(int[] nums, int start) {
        // 回溯算法标准框架
        for (int i = start; i < nums.length; i++) {
            // 剪枝逻辑,跳过值相同的相邻树枝
            if (i > start && nums[i] == nums[i - 1]) {
                continue;
            }
            // 做选择
            track.addLast(nums[i]);
            // 注意参数
            backtrack(nums, i + 1);
            // 撤销选择
            track.removeLast();
        }
    }
    
    Arrays.sort(nums);
    /* 排列问题回溯算法框架 */
    void backtrack(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            // 剪枝逻辑
            if (used[i]) {
                continue;
            }
            // 剪枝逻辑,固定相同的元素在排列中的相对位置
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
                continue;
            }
            // 做选择
            used[i] = true;
            track.addLast(nums[i]);
    
            backtrack(nums);
            // 撤销选择
            track.removeLast();
            used[i] = false;
        }
    }
    
有很多人对上述剪枝操作不理解,看了这幅图你就会豁然开 

  • 元素无重可复选(nums中的元素都是唯一的,每个元素可以被使用若干次)
    /* 组合/子集问题回溯算法框架 */
    void backtrack(int[] nums, int start) {
        // 回溯算法标准框架
        for (int i = start; i < nums.length; i++) {
            // 做选择
            track.addLast(nums[i]);
            // 可以复选,所以i不用+1作为参数
            backtrack(nums, i);
            // 撤销选择
            track.removeLast();
        }
    }
    
    /* 排列问题回溯算法框架 */
    void backtrack(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            // 做选择
            track.addLast(nums[i]);
            backtrack(nums);
            // 撤销选择
            track.removeLast();
        }
    }

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃橘子的Crow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值