LeetCode之回溯算法

回溯法也可以叫做回溯搜索法,它是一种搜索的方式。回溯是递归的副产品,只要有递归就会有回溯。因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。

回溯法,一般可以解决如下几种问题:

组合问题:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等

回溯搜索的遍历过程:


回溯算法模板框架如下:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

题解:
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> ans = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {
        dfs(nums, 0);
        return result;
    }

    public void dfs(int[] nums, int index) {
        if (index == nums.length) {
            //到最底一级分支了
            result.add(new ArrayList<Integer>(ans));
            return;
        }

        ans.add(nums[index]);
        dfs(nums, index+1); //继续向下一级搜索
        ans.remove(ans.size()-1);  //执行结束后需要对 ans 进行回溯
        dfs(nums, index+1);
    }
}

全排列

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

题解:
class Solution {
    /**
     * 思路:
     * n个数全排列,从数学上想,一共有n!种排列方式,
     * 因为第一个位置有n个选择,第二个位置有(n-1)个选择,以此类推,所有一共有 n*(n-1)*(n-2)*2*1
     * 解题过程就是要模拟这个过程
     *
     * baseCase  处理完最后一个元素,把当前Array做一份copy添加到答案里
     * backtrack(array, index)
     * 从i= [index, n]之间依次选取所有元素进行搜索
     * 每次将i和index的元素交换 -> next_state
     * dfs(array, index+1)
     * 将i和index元素交换回来 -> restore state (回溯状态)
     * @param nums
     * @return
     */
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();  //最终返回结果
        dfs(result, nums, 0);
        return result;
    }

    /**
     * 回溯算法过程
     * @param result
     * @param nums
     * @param index
     */
    private void dfs(List<List<Integer>> result, int[] nums, int index) {
        if (index >= nums.length) {
            //当前分支搜索完毕,将当前集合遍历,并存入总集合中
            List<Integer> ans = new ArrayList<>();
            for (int i=0;i<nums.length;i++) {
                ans.add(nums[i]);
            }
            result.add(ans);
            return;
        }

        //搜索没有结束,将当前放的位置和剩下的数每一个进行交换,并且调用当前层级的dfs,然后再交换回来
        for (int i=index;i<nums.length;i++) {
            swap(nums, index, i);
            dfs(result, nums, index+1);
            swap(nums, i, index);
        }
    }

    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}
参考:

https://www.bilibili.com/video/BV1Ty4y1E7RM/?spm_id_from=333.337.search-card.all.click&vd_source=40c24e77b23dc2e50de2b7c87c6fed59

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章