回溯算法(剑指offer)

剑指offer之回溯算法

运用回溯法解题的关键要素有以下三点:
(1) 针对给定的问题,定义问题的解空间;
(2) 确定易于搜索的解空间结构;
(3) 以深度优先方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。
回溯法的基本思想:能进则进,不进则退,再换一条路走。(即按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择。)

矩阵中的路径

题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如
在这里插入图片描述
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
思路:注意到题目中的要求行走的路径不能重复进入某格子,因此需要创建一个用来存放该点状态(即是否已被访问过,默认为false,已访问则为true)的矩阵,且矩阵大小与原矩阵相对应。
路径的每一次移动规则:只能向上,向下,向左,向右移动一格。
我们可以将路径的行走过程看成是入栈的过程,当矩阵已定位了字符串的前n个字符后,由第n个字符所对应的矩阵的位置分别向上,向下,向左,向右进行试探查找对应第n+1个字符的位置,如果都没有,路径需要回退到第n-1个字符,将原始的第n个字符的标记flag进行调整,并且重新定位第n个字符。
注意给定函数中传入的矩阵并不是以二维的形式传入,而是将矩阵转换为了一维数组,并给定了行和列信息,以此来确定输入的矩阵格式。 因此代码中的表示矩阵中的某一个元素序号使用index = row*cols + col;

public class Solution {
     /*判断字符矩阵是否包含某一个字符序列
     */
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
        boolean[] flag = new boolean[matrix.length];
        for(int row = 0 ;row < rows; row++){
            for(int col = 0; col < cols;col++){
                if(hasPathChar(matrix,rows,cols,row,col,str,0,flag))
                    return true;
            }
        }
        return false;
    }
 
    /**
     * 回溯法递归实现判断
     * @param matrix    字符矩阵
     * @param rows  矩阵行数
     * @param cols  矩阵列数
     * @param row   当前行索引
     * @param col   当前列索引
     * @param str   目标字符序列
     * @param k 目标字符序列中当前字符索引
     * @param flag    字符矩阵是否被访问过标记
     * @return
     */
   public static boolean hasPathChar(char[] matrix,int rows,int cols,int row,int col,char[] str,int k,boolean[] flag){
       int index = row*cols + col;
       //超界判定
       if(row < 0 ||col < 0||row >= rows || col >= cols || flag[index]||matrix[index] != str[k]){
           return false;
       }
       //终止循环的条件
       if(k == str.length - 1){
           return true;
       }
       flag[index] = true;
       k++;
       //在当前字符的上下左右进行搜索匹配,递归实现
       if(hasPathChar(matrix,rows,cols,row-1,col,str,k,flag)||
         hasPathChar(matrix,rows,cols,row+1,col,str,k,flag)||
         hasPathChar(matrix,rows,cols,row,col-1,str,k,flag)||
         hasPathChar(matrix,rows,cols,row,col+1,str,k,flag)){
          
           return true;
       }
       // 在当前字符的上、下、左、右的元素没有搜索到下一个目标字符,将访问标记重置为false,并且返回false;
       flag[index] = false;
       return false;
   }
}

机器人的运动范围

题目描述
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
思路:
本题思路与上一题大致相同:
都需要定义一个字符矩阵用来标记该点是否被访问过
迭代函数中都需要在开始处进行越界判定
进入迭代函数表示判断已经进行一次了。
总之利用递归实现,每次只能走上下左右四个点,进行判断点的位置是否越界,点数之和是否大于K,是否已经走过了。

public class Solution {
    public int movingCount(int threshold, int rows, int cols){
        boolean[][] flag = new boolean[rows][cols];
        return hasCore(0,0,rows,cols,threshold,flag);
    }
    public int hasCore(int row,int col,int rows,int cols,int threshold,boolean[][] flag){
        if(row<0 ||col<0 ||row>=rows ||col>=cols || numSum(row)+numSum(col)>threshold ||flag[row][col])
            return 0;
        flag[row][col] = true;
        return  hasCore(row-1,col,rows,cols,threshold,flag)+
          hasCore(row+1,col,rows,cols,threshold,flag)+
          hasCore(row,col-1,rows,cols,threshold,flag)+
          hasCore(row,col+1,rows,cols,threshold,flag)+1;
            
        
    }
    public int numSum(int i){
        int sum = 0;
        while(i>0){
            sum += i%10;
            i = i/10;
        }
        return sum;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值