【算法题】回溯算法学习总结-2

82 阅读1分钟

不做舔狗,好好学习,冲!

leetcode 46. 全排列

image.png

思路:

  • 很明显的使用回溯算法,该题简单就简单在不包含重复元素
public List<List<Integer>> permute(int[] nums) {
    List<Integer> stack = new ArrayList<>();
    List<List<Integer>> result = new ArrayList<>();
    backtrack(nums, stack, result);
    return result;
}

public void backtrack(int[] nums, List<Integer> stack, List<List<Integer>> result){
    if (stack.size() == nums.length){
        result.add(new ArrayList<>(stack));
    }
    for (int i =0;i<nums.length;i++){
        int num = nums[i];
        if (stack.contains(num))continue;
        stack.add(num);
        backtrack(nums, stack, result);
        stack.remove(stack.size() -1);
    }
}

leetcode 47. 全排列 II

image.png

思路

  • 这个比上一个题就难得多了,他包含重复元素,最终解里还不能有重复元素,要考虑去重,当然可以用set,但是估计run不过全部,需要想办法剪枝
  • 去重思考:(后续也能用到)
    • 排序,保证重复的数排在一起
    • 在回溯算法中,如果stack中不包含前一个元素,说明前一个元素要么进入最终结果了,要么不满足要求,如果此时nums[i-1] = nums[i],选择这个i加入stack一定是重复的
public static List<List<Integer>> permuteUnique2(int[] nums) {
    List<Integer> stack = new ArrayList<>();
    List<List<Integer>> result = new ArrayList<>();
    Arrays.sort(nums);
    backtrack47(nums, stack, result, 0);
    return result;
}

private static void backtrack47(int[] nums, List<Integer> stack, List<List<Integer>> result, int index) {
    if (stack.size() == nums.length){
        List<Integer> list = new ArrayList<>();
        for (int i : stack){list.add(nums[i]);}
        result.add(list);
        return;
    }

    for (int i =0;i<nums.length;i++){
        int num = nums[i];
        if (stack.contains(i))continue;
        if (i > 0 && nums[i-1] == num && !stack.contains(i-1)){continue;}
        stack.add(i);
        backtrack47(nums, stack, result,i+1);
        stack.remove(stack.size() -1);
    }
}
  • 另一种思路,用一个数组vis保存该位置是否被使用过,如果nums[i-1] == nums[i],切vis[i-1]使用过了,那么此时stack加入i,一定也是重复的解
//47. 全排列 II
    public static List<List<Integer>> permuteUnique(int[] nums) {
        List<Integer> stack = new ArrayList<>();
        List<List<Integer>> result = new ArrayList<>();
        boolean[] vis = new boolean[nums.length];
        backtrack47(nums, stack, result, 0, vis);
        return result;
    }

    private static void backtrack47(int[] nums, List<Integer> stack, List<List<Integer>> result, int index, boolean[] vis) {
        if (stack.size() == nums.length){
            List<Integer> list = new ArrayList<>();
            for (int i : stack){list.add(nums[i]);}
            result.add(list);
            return;
        }

        for (int i =0;i<nums.length;i++){
            int num = nums[i];
            if (stack.contains(i))continue;
            if (vis[i] || (i>0 && nums[i-1]==nums[i] && vis[i-1])) continue;
            stack.add(i);
            vis[i] = true;
            backtrack47(nums, stack, result,i+1,vis);
            stack.remove(stack.size() -1);
            vis[i]=false;
        }
}

这两种思路都能run,都可以借鉴

leetcode 77. 组合

image.png

思路:

  • 和46没啥区别,很简单的题目
public List<List<Integer>> combine(int n, int k) {
    List<Integer> stack = new ArrayList<>();
    List<List<Integer>> result = new ArrayList<>();
    int[] arr = new int[n];
    for (int i =0;i<n;i++) {arr[i] = i+1;}
    backtrack77(arr, stack, result, k, 0);
    return result;
}

private void backtrack77(int[] arr, List<Integer> stack, List<List<Integer>> result, int k, int index) {
    if (stack.size() == k) {
        result.add(new ArrayList<>(stack));
        return;
    }
    for (int i =index;i<arr.length;i++){
        int num = arr[i];
        if (stack.contains(num)) { continue; }
        stack.add(num);
        backtrack77(arr, stack,result, k,i+1);
        stack.remove(stack.size() -1);
    }
}

leetcode 78. 子集

image.png

思路

  • 这道题也很简单,能run100%也是没想到
  • 关键就是这种情况下,解加入到result中后,不要ruturn,继续回溯
public List<List<Integer>> subsets(int[] nums) {
    List<Integer> stack = new ArrayList<>();
    List<List<Integer>> result = new ArrayList<>();
    backtrack78(nums, stack, result, 0);
    return result;
}

private void backtrack78(int[] nums, List<Integer> stack, List<List<Integer>> result, int index) {
    if (stack.size() <= nums.length){
        result.add(new ArrayList<>(stack));
    }
    for (int i = index;i< nums.length;i++){
        int num = nums[i];
        if (stack.contains(num)) continue;
        stack.add(num);
        backtrack78(nums, stack, result, i+1);
        stack.remove(stack.size() -1);
    }

}

leetcode 90. 子集 II

image.png

思路:

  • 这道题借鉴了78题和47题的去重思路:
  • 去重思考:(和47题一样的思路)
    • 排序,保证重复的数排在一起
    • 在回溯算法中,如果stack中不包含前一个元素,说明前一个元素要么进入最终结果了,要么不满足要求,如果此时nums[i-1] = nums[i],选择这个i加入stack一定是重复的
public List<List<Integer>> subsetsWithDup(int[] nums) {
    List<Integer> stack = new ArrayList<>();
    List<List<Integer>> result = new ArrayList<>();
    Arrays.sort(nums);
    backtrack90(nums, stack, result, 0);
    return result;
}
private void backtrack90(int[] nums, List<Integer> stack, List<List<Integer>> result, int index) {
    if (stack.size() <= nums.length){
        List<Integer> list = new ArrayList<>();
        for (int i : stack){list.add(nums[i]);}
        result.add(list);
    }
    for (int i = index;i< nums.length;i++){
        int num = nums[i];
        if (stack.contains(i)) continue;
        if (i > 0 && !stack.contains(i-1) && nums[i-1] == num) continue;
        stack.add(i);
        backtrack90(nums, stack, result, i+1);
        stack.remove(stack.size() -1);
    }

}

做题上瘾,本来想复习juc包的,看看晚上有没有时间复习吧!