Back
Featured image of post Backtrack回溯 全排列

Backtrack回溯 全排列

backtrack

backtrack 回溯算法

先做决定,然后撤回 , 就是一个决策树的遍历

需要注意的有三个问题

  • 路径

  • 选择

  • 终止条件

    伪码如下:

    public void backtrack(路径选择列表){
        if (终止条件){
            add(路径)
            return;
        }
        for 选择 in 列表 {
            做选择
            backtrack(路径选择列表);
            撤回
        }
    }
    

而且不管怎么优化,都符合回溯框架,而且时间复杂度都不可能低于 O(N!),因为穷举整棵决策树是无法避免的。这也是回溯算法的一个特点,不像动态规划存在重叠子问题可以优化,回溯算法就是纯暴力穷举,复杂度一般都很高


46. 全排列 题目要求不含重复数字,所以我们还需要注意不符合的选择

class Solution {
    public List<List<Integer>> res = new LinkedList<>();

    public List<List<Integer>> permute(int[] nums){
        LinkedList<Integer> track = new LinkedList<>();
        backtrack(nums , track);
        return res;
    }

    private void backtrack(int[] nums, LinkedList<Integer> track) {
        // 触发结束条件
        if (track.size() == nums.length) {
            res.add(new LinkedList(track));
            return;
        }

        for (int i = 0; i < nums.length; i++) {
            // 排除不合法的选择
            if (track.contains(nums[i]))
                continue;
            // 做选择
            track.add(nums[i]);
            // 进入下一层决策树
            backtrack(nums, track);
            // 取消选择
            track.removeLast();
        }
    }
}

47. 全排列 II 而在47中,是包含了重复数字

class Solution {
    boolean[] record;
    List<List<Integer>> ans = new LinkedList<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        int n = nums.length;
        if (n == 0){
            return ans;
        }
        //剪枝前需排序
        Arrays.sort(nums);
        LinkedList<Integer> track = new LinkedList<>();
        record = new boolean[n];
        backtrack(nums , track);
        return ans;
    }

    private void backtrack(int[] nums , LinkedList<Integer> list){
        if (nums.length == list.size()){
            ans.add(new LinkedList<>(list));
            return;
        }

        for (int i = 0; i < nums.length; i++) {
            if (record[i]){
                continue;
            }
            //剪枝,!record[i - 1]为顺序剪枝,record[i - 1]为倒序剪枝
            if (i > 0 && nums[i] == nums[i - 1] && !record[i - 1]) {
                continue;
            }
            record[i] = true;
            list.add(nums[i]);
            backtrack(nums , list);
            record[i] = false;
            list.removeLast();
        }
    }
}
Welcome to the world of Minezeratul