[算法分享]了解这个,回溯刷疯了

回溯

回溯,计算机算法,回溯法也称试探法,它的基本思想是:从问题的某一种状态(初始状态)出发,搜索从这种状态出发所能达到的所有“状态”,当一条路走到“尽头”的时候(不能再前进),再后退一步或若干步,从另一种可能“状态”出发,继续搜索,直到所有的“路径”(状态)都试探过。这种不断“前进”、不断“回溯”寻找解的方法,就称作“回溯法”。(来自百度知道)
是不是感觉很麻烦,直接引入例子吧。

例子引入

全排列(leetcode 46)。

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

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

让我们来解解这道题:
在这里插入图片描述
由此可以看出这是一棵树,所以我们用树的知识看待这道题。
如果这个数不存在,我们就会往下递归,然后重新从这个数组遍历,如果达到条件(这里条件我们可以看出当数组全部被我们遍历,结果数组长度等于nums长度)的时候就停止递归,或者如果数字存在于其中时候,就停止遍历,并且退回于原来数字,比如1->2->3结束后就会退回到1然后往下递归三,这种停止遍历回退的我们叫做剪枝,就好比树一样,当你减掉枝条其他枝条和树叶就不会在树上了,也就不会往后递归遍历了。
我们通过上面的想法来写写代码

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> lists = new ArrayList<>();//用来存放结果
        List<Integer> list = new ArrayList<>();//用来充当当前路径
        permute(nums, lists, list);
        return lists;
    }
    private void permute(int[] nums, List<List<Integer>> lists,List<Integer> list){
        if(list.size()==nums.length){//满足条件
            lists.add(new ArrayList<>(list));//将当前路径加入到结果
            return;//递归退出
        }
        for (int num : nums) {
            if (list.contains(num)) continue;//判断是否有当前数
            list.add(num);//加入当前数
            permute(nums, lists, list);//进入递归
            list.remove((Integer) num);//还原结果
        }

    }
}

我们可以通过上面的代码得出一个回溯的模型:

   	主方法() {
   	初始化;
   	切入点方法();
    }
    切入点方法(){
        if(满足条件){
            加入结果;
            return;//递归退出
        }
        for () {
            基本判断 ;
            加入结果;
            切入点方法();
            还原结果;
        }

    }

来做几道题练练手

组合(leetcode77)

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。

输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

满足条件:当前路径=k
更具条件和模板可以得出代码:

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> lists = new ArrayList<>();
        List<Integer> list = new ArrayList<>();
        combine(n,k,lists,list,1);
        return  lists;

    }
    private void combine(int n,int k,List<List<Integer>> lists ,List<Integer> list,int index){
        if(list.size()==k){
            lists.add(new ArrayList<>(list));
            return;
        }
        for (int i = index; i <=n; i++) {
            list.add(i);
            combine(n,k,lists,list,i+1);
            list.remove((Integer) i);
        }
    }
}

都觉得这些题太简单了对吧
来一道难一点的

N 皇后(leetcode 51)

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

输入:n = 4
输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
解释:4 皇后问题存在两个不同的解法.

满足条件:
1.List达到n个元素
2.正斜角和反斜角,上下都不能同时存在两个Q(正斜角的x-y相等,反斜角的x+y相等)
根据上面两个条件可以得出数组一定是一个无重复数字的数组,所以可以得出代码为

class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> lists = new ArrayList<>();
        List<List<Integer>> ILists = new ArrayList<>();
        solveNQueens(n,new ArrayList<>(),ILists);
        toStringList(ILists,lists,n);
        return lists;
    }

    private void solveNQueens(int n, List<Integer> IList, List<List<Integer>> ILists) {
        if (IList.size() == n) {
            if (isTrue(IList)) {
                ILists.add(new ArrayList<>(IList));
            }
            return;
        }
        for (int i = 0; i < n; i++) {
            if(IList.contains(i))continue; //取出重复数字(上下不能重复)
            IList.add(i);
            solveNQueens(n,IList,ILists);
            IList.remove((Integer) i);
        }

    }

    private boolean isTrue(List<Integer> IList) {
        for (int i = 0; i < IList.size(); i++) {
            for (int j = 0; j < IList.size(); j++) {
                if (j == i) continue;
                int x = i + IList.get(i);
                int y = i - IList.get(i);
                int xj = j + IList.get(j);
                int yj = j - IList.get(j);
                if(x==xj||yj==y)return false; //(如果相等,说明斜角有相同的皇后)
            }
        }
        return true;
    }
    private void toStringList(List<List<Integer>> ILists,List<List<String>> lists,int n){
        for (List<Integer> IList : ILists) {
            ArrayList<String> list = new ArrayList<>();
            for (int j = 0; j < n; j++) {
                StringBuilder sb = new StringBuilder();
                for (int k = 0; k < n; k++) {
                    if (IList.get(j) == k) {
                        sb.append("Q");
                        continue;
                    }
                    sb.append(".");
                }
                list.add(sb.toString());
            }
            lists.add(list);
        }
    }
}

通过这种模板写回溯,我感觉我刷疯了,你可以去试试,不会可以私信我,如果你也对回溯刷疯了也可以来私信我。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

怀特丶恩佐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值