算法 *-* 回溯算法 (类似枚举的搜索过程,通解通法美称)

回到顶部(go to top)

什么是回溯算法

对于回溯算法的定义,百度百科上是这样描述的:回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称

 

看明白没,回溯算法其实就是一个不断探索尝试的过程,探索成功了也就成功了,探索失败了就在退一步,继续尝试……,

回到顶部(go to top)

必看解析

1-什么叫回溯算法,一看就会,一写就废

2-回溯算法详解(修订版)

 

回溯算法的框架:

1
2
3
4
5
6
7
8
9
10
result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
 
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

 

回到顶部(go to top)

不同数据结构的 “撤销刚刚的选择” 

1.ArrayList (所有实现List接口的实现类)

ArrayList<Integer> track

track.remove(track.size()-1);

 

2.StringBuilder

StringBuilder builder

builder.deleteCharAt(cur.length() - 1);

 

回到顶部(go to top)

必看例子

Attention: 注意“全排列”和“所有子集”中backtrack()的参数区别。

  • “所有子集”中有一个start参数,代表遍历到了哪里,不走回头路,是为了防止重复
  • “全排列”本身就是各种元素的排序,元素肯定是重复的,所有就没有这个start参数,索引始终从0开始。

 

全排列

LeetCode 46 - 全排列  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        //暂存结果
        ArrayList<Integer> track = new ArrayList<>();
        //最终结果
        List<List<Integer>> result = new ArrayList<>();
        //回溯函数
        backtrack(nums, track, result);
        return result;
    }
 
    private void backtrack(int[] nums, ArrayList<Integer> track, List<List<Integer>> result){
        //end condition
        if(track.size() == nums.length){
            result.add(new ArrayList(track));
            return;
        }
 
        //【重要】每次索引都从0开始!!!这里和“所有子集”问题不同
        for(int i = 0; i < nums.length; i++){
            //用contains判断是否存在
            if(! track.contains(nums[i])){
                track.add(nums[i]);
                backtrack(nums, track, result);
                //删除容器里,最后一个索引的值
                track.remove(track.size()-1);
            }
        }
    }
}

 

 

所有子集

Leetcode 78 - 子集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> track = new ArrayList<>();
 
        backtrack(nums, 0, track, res);
 
        return res;
 
    }
<br>    //【重要】注意这个参数start
    private void backtrack(int[] nums, int start, List<Integer> track, List<List<Integer>> res){
        //每一个路径,都是一个解
        //【重要】这里没有明显的一个递归出口,而是靠下面的for循环的终止条件
        //【重要】因为track是公用的,所以在加入res前,需要复制一份新的
        res.add(new ArrayList(track));
        <br>        //每次递归不是从0开始,而是start
        for(int i = start; i < nums.length; i++){
            //重要】i 已经放在了track暂时解中,因此在此前提下,之后的递归就要exclude这个i,那就i+1即可
            track.add(nums[i]);
            //System.out.println("track add " + nums[i]);
            backtrack(nums, i+1, track, res);
            track.remove(track.size() - 1);
        }
 
    }
}

 

posted on   frank_cui  阅读(309)  评论(0编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

levels of contents
点击右上角即可分享
微信分享提示