数据结构 回溯搜索算法

1、回溯搜索法能解决的问题

      (1)组合问题:例如1,2,3,4中有几种长度为2的组合方式12,13,14,23,24,34。不需要顺序

      (2)切割问题:讲一个字符串切割后,保证其切割的子串都是回文子串。

      (3)子集问题:例如1,2,3,4中所有的子集。不需要顺序

      (4)排列问题:例如1,2,3,4中几种长度为2的组合方式12,13,14,23,24,34,31,21,41,32,42,43。需要顺序。

      (4)棋盘问题:n皇后问题

使用循环很难解决上述的问题,因此以上问题必须使用回溯法来解决。

2、回溯搜索法的思路

回溯法可以抽象为一个树形结构,回溯可以理解为一棵树的横向宽度的for循环和纵向深度的递归。

 3、写代码的一般套路(模板)

public void backtracking()//定义回溯函数

{

       //1、终止条件

         if(终止条件)

         {

                收集结果;

                return ;

         }
 

      //2、for循环执行处理节点,递归函数,回溯函数

         for(遍历集合元素)

        {

                处理节点

               递归函数

               回溯函数

        }

        return 最终的遍历结果。
 

}

4、回溯算法的应用-组合问题

首先提出问题,取出1,2,3,4中所有的长度为2的集合

代码如下:

for(int i=0;i<num.length;i++)

{

             for (int j=i;j<num.length;j++)

              {

                    System.out.print(i);

                    System.out.print(j);
              }
}

但是如果我们将上面的数组换为100个数的长度为50的组合就需要50层的for循环进行嵌套,这显然是不现实的。

这个时候我们就需要进行回溯算法

解释一下这个图,为什么每取一个值的时候,后面的分枝就没有这个数了呢,因为,组合的定义是组合是没有顺序的,因此如果不讲前边的数组丢点必然会造成数据的重复。

代码实现

递归函数参数返回值

确定终止条件

单层递归函数

源代码:

List<List<Integer>> result = new ArrayList<List<Integer>>();
List<Integer> path= new ArrayList<Integer>();
List<Integer> path2;
public List<List<Integer>> combine(int n, int k)
{
    backstracking(n,k,1);
    return result;

}
public void  backstracking(int n,int k,int startindex)
{
    if(path.size()==k)
    {
        path2=new ArrayList<Integer>();
        for (int i: path)
        {
            path2.add(i);

        }

        result.add(path2);
        return ;
    }
    for(int i=startindex;i<=n;i++)
    {
        path.add(i);
        backstracking(n,k,i+1);//这里为什么是i+1呢因为不能出现两个字母的重复,如果是i的话就会出现11,22,33这种情况。
        path.remove(path.size()-1);

    }
}

5、回溯算法的应用-组合问题的剪枝问题

如下图:如果当n=4,k=4的时候再进行纯暴力的搜索,会极大的浪费计算机的资源,因此需要对这个算法进行优化,使得这个算法的时间复杂度变低。

 要改的地方主要是size改为n-(k-path.size)+1;

6、回溯算法的应用-组合总和

场景:

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。

思路:

代码编写思路

递归函数参数返回值

确定终止条件

单层递归函数

源代码:

import java.util.*;
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) 
    {
        int sum=0;
        int startindex=0;
        backtracking(candidates,target,sum,startindex);
        return result;

    }
    List<List<Integer>> result=new ArrayList<List<Integer>>();
    List<Integer> path =new ArrayList<Integer>();
    List<Integer> path2;
    public void backtracking(int[] candidates, int target,int sum,int startindex)
    {
        //首先确定函数的参数以及返回值
        if(sum>target)
        {
            return ;
        }
        //确定终止条件
        if(sum==target)
        {
            path2=new ArrayList<Integer>(); 
            for(int i: path)
            {
                path2.add(i);
            }
            result.add(path2);
            return;

        }
        //确定单层递归函数
        for(int i=startindex;i<candidates.length;i++)
        {
            path.add(candidates[i]);
            sum=sum+candidates[i];
            backtracking(candidates,target,sum,i);
            sum=sum-candidates[i];
            path.remove(path.size()-1);

        }
        return ;
    }
}

 

猜你喜欢

转载自blog.csdn.net/qq_35677589/article/details/112786004