D21&&D22|开始回溯

回溯基础知识

1.什么是回溯:回溯法是一个纯暴力的搜索方式,有时候一些问题只能靠回溯暴力法来解决

2.适用于什么问题:

1)N个数里面按一定规则找出k个数的集合

2)一个字符串按一定规则有几种切割方式

3)一个N个数的集合里有多少符合条件的子集

4)N个数按一定规则全排列,有几种排列方式

5)N皇后,解数独等等

3.回溯法模板

)回溯返回值以及参数

返回值一般是void

参数一般是根据题目需要再进行确定

2)回溯函数终止条件

什么时候达到了终止条件,树中就可以看出,一般来说搜到叶子节点了,也就找到了满足条件的一条答案,把这个答案存放起来,并结束本层递归。

3)回溯搜索的遍历过程

D21&&D22|开始回溯_第1张图片


第77题.组合

初始思路:

        这道题会比较像收集路径的题目,但是对于如何把这道题构思为树的结构没有什么概念,初始思路就是第一层for循环表示n=4这四个数,也是k=2中的第一个数,然后通过回溯进入第二层循环,需要一个startindex,因为第二层循环中是n=3三个数,然后k=2作为结束条件就是收集路径的结合大小为2时结束,将路径收集在result集合中。

class Solution {
    //跟收集路径很像
    List> result = new ArrayList<>();
    List path = new ArrayList<>();
    public List> combine(int n, int k) {
        backtracking(n,k,1);
        return result;

    }
    public void backtracking(int n, int k, int start){
        if(path.size()==k){
            result.add(new ArrayList(path));
            return ;
        }
        for(int i=start;i<=n;i++){
            path.add(i);
            backtracking(n,k,i+1);
            path.remove(path.size()-1);
        }

    }
}

这里注意一定要new一个arraylist装入result数组中!不然就会原始的变,result中也变

就是12的时候result也是(12)13的时候result中就变成了(1,3)(1,3),1,4的时候

result中就变成了(1,4)(1,4)(1,4)。

题解复盘:

对于将组合问题抽象为树形结构有了更清楚地认知。

D21&&D22|开始回溯_第2张图片

剪枝优化

D21&&D22|开始回溯_第3张图片

每一个节点代表一个for循环

当K的数值比较大时就可以对不满足深度的节点进行剪枝,从而降低运算量。

接下来看一下优化过程如下:

  1. 已经选择的元素个数:path.size();

  2. 所需需要的元素个数为: k - path.size();

  3. 列表中剩余元素(n-i) >= 所需需要的元素个数(k - path.size())

  4. 在集合n中至多要从该起始位置 : i <= n - (k - path.size()) + 1,开始遍历

为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。

举个例子,n = 4,k = 3, 目前已经选取的元素为0(path.size为0),n - (k - 0) + 1 即 4 - ( 3 - 0) + 1 = 2。

从2开始搜索都是合理的,可以是组合[2, 3, 4]。


216.组合总和III

初始思路:

本题跟组合问题基本一致,结束条件仍为path.size()=k,仅是在其中添加判断,只有和为n的才添加进result中。

一些容易犯错误的点:

1,结束条件中一定要写return!!,不然就会stackoverflow

class Solution {
    List> result = new ArrayList<>();
    List path = new ArrayList<>();
    public List> combinationSum3(int k, int n) {
        backtracking(k,n,1);
        return result;
    }
    public void backtracking(int k, int n, int startindex){
        int sum = 0;
        if(path.size()==k){
            for(int i:path){
                sum = sum+i;
            }
            if(sum==n){
                result.add(new ArrayList(path));
            }
            return ;
        }
        for(int i=startindex;i<=10-(k-path.size());i++){
            path.add(i);
            backtracking(k,n,i+1);
            path.remove(path.size()-1);
        }

    }
}

17.电话号码的字母组合

直接看视频

题解复盘:

1.字母和数字如何对应:

/初始对应所有的数字,为了直接对应2-9,新增了两个无效的字符串""
        String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};

2.树的架构如何理解

 D21&&D22|开始回溯_第4张图片

 3.结束条件就是遍历到叶子节点了,也就是数字String的长度。

class Solution {

    //设置全局列表存储最后的结果
    List list = new ArrayList<>();

    public List letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) {
            return list;
        }
        //初始对应所有的数字,为了直接对应2-9,新增了两个无效的字符串""
        String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        //迭代处理
        backTracking(digits, numString, 0);
        return list;

    }

    //每次迭代获取一个字符串,所以会设计大量的字符串拼接,所以这里选择更为高效的 StringBuild
    StringBuilder temp = new StringBuilder();

    //比如digits如果为"23",num 为0,则str表示2对应的 abc
    public void backTracking(String digits, String[] numString, int num) {
        //遍历全部一次记录一次得到的字符串
        if (num == digits.length()) {
            list.add(temp.toString());
            return;
        }
        //str 表示当前num对应的字符串
        String str = numString[digits.charAt(num) - '0'];
        for (int i = 0; i < str.length(); i++) {
            temp.append(str.charAt(i));
            //c
            backTracking(digits, numString, num + 1);
            //剔除末尾的继续尝试
            temp.deleteCharAt(temp.length() - 1);
        }
    }
}

你可能感兴趣的:(算法)