剑指offer面试题13(java版):机器人的运动范围

welcome to my blog

剑指offer面试题13(java版):机器人的运动范围

题目描述

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

第四次做; DFS; 需要记录访问过的节点, 但并不是回溯, 只需要改变现场, 不需要恢复现场

//DFS, 递归实现; 需要记录访问过的节点, 但并不是回溯, 只需要改变现场, 不需要恢复现场
class Solution {
    private int res;
    public int movingCount(int m, int n, int k) {
        //访问过的节点不能再访问了
        boolean[][] flag = new boolean[m][n];
        core(flag, m, n, k, 0, 0);
        return res;
    }
    /*
    递归函数逻辑:判断当前坐标(i,j)是否合法(当前条件), 不合法则不处理, 合法则处理(i,j)周围的坐标(新条件新递归)
    */
    private void core(boolean[][] flag, int m, int n, int k, int i, int j){
        //坐标越界 || 该点访问过 || 数位之和大于k
        if(i<0 || i>= m || j<0 || j>=n || flag[i][j]==true ||digitSum(i,j)>k)
            return;
        //改变现场
        flag[i][j]=true;
        //最终结果++
        res++;
        //探索周围的节点(新条件新递归)
        core(flag,m,n,k,i-1,j);
        core(flag,m,n,k,i+1,j);
        core(flag,m,n,k,i,j-1);
        core(flag,m,n,k,i,j+1);
    }
    private int digitSum(int a, int b){
        int res = 0;
        while(a!=0){
            res += a%10;
            a /= 10;
        }
        while(b!=0){
            res += b%10;
            b /= 10;
        }
        return res;
    }
}

思路

  • 递归的理解: 递归函数中, 先处理当前实参表示的状态, 再对下一个(多个)状态调用递归函数

复杂度

  • 时间复杂度
  • 空间复杂度: 创建了boolean[], 所以为O(n)

第三次做, 开始忘了改变(i,j)的访问状态陷入了无限递归

public class Solution {
    int count=0;
    public int movingCount(int threshold, int rows, int cols)
    {
         if(threshold<0)
             return 0;
        boolean[] flag = new boolean[rows*cols];
        Core(threshold, rows, cols, flag, 0, 0);
        return count;
    }
    public void Core(int threshold, int rows, int cols, boolean[] flag, int i, int j){
        if(i<0 || i>=rows || j<0 || j>=cols)
            return;
        int coordinate = i * cols + j;
        if(digitSum(i,j)>threshold){
            return;
        }
        if(flag[coordinate] == true)
            return;
        //开始忘了改变状态, 陷入了无限递归
        flag[coordinate] = true;
        count++;
        Core(threshold, rows, cols, flag, i-1, j);
        Core(threshold, rows, cols, flag, i+1, j);
        Core(threshold, rows, cols, flag, i, j-1);
        Core(threshold, rows, cols, flag, i, j+1);
    }
    public int digitSum(int i, int j){
        int sum=0;
        while(i!=0){
            sum += i%10;
            i /= 10;
        }
        while(j!=0){
            sum += j%10;
            j /= 10;
        }
        return sum;
    }
}

第二次做, 创建二维数组记录已访问过的点

public class Solution {
    public int count=0;
    public int movingCount(int threshold, int rows, int cols)
    {
        boolean[][] flag = new boolean[rows][cols];
        Core(threshold, rows, cols, 0, 0, flag);
        return count;
    }
    public void Core(int threshold, int rows, int cols, int i, int j, boolean[][] flag){
        //base case
        if(i<0 || i>rows-1 || j<0 || j>cols-1)
            return;
        if(flag[i][j]==true)
            return;
        if(digitSum(i,j)>threshold)
            return;
        count++;
        flag[i][j]=true;
        Core(threshold, rows, cols, i-1, j, flag);
        Core(threshold, rows, cols, i+1, j, flag);
        Core(threshold, rows, cols, i, j-1, flag);
        Core(threshold, rows, cols, i, j+1, flag);
    }
    public int digitSum(int i, int j){
        int sum = 0;
        while(i!=0){
            sum += i%10;
            i /= 10;
        }
        while(j!=0){
            sum += j%10;
            j /= 10;
        }
        return sum;
    }
}
public class Solution {
    public int movingCount(int threshold, int rows, int cols)
    {
        boolean[] flag = new boolean[rows*cols];
        for(int i=0 ; i<rows*cols; i++)
            flag[i]=false;
        int result = movingCountCore(threshold, rows, cols, 0, 0, flag);
        return result;
    }
    private int movingCountCore(int threshold, int rows, int cols, int i, int j, boolean[] flag){
        // 从(i,j)开始,能否向上下左右移动? 可以同时探索多个方向
        // 走过的地方就不能再走了, 注意标识一下flag
        // 判断(i,j)是否越界, (i,j)能否到达, (i,j)数位和是否大于threshold
        int index = i*cols+j;
        if(i<0 || i>= rows || j<0 || j>=cols || flag[index]==true || sumLargerThanK(i, j, threshold))
            return 0;
        // 执行到这里说明(i,j)可以到达, 对应的flag置true, 表示不能再到达(i,j)
        flag[index] = true;
        // 计数加一, 之后继续递归探索(i,j)上下左右的点
        int count = 0;
        count = 1 + movingCountCore(threshold, rows, cols, i+1, j, flag) +
            movingCountCore(threshold, rows, cols, i-1, j, flag) +
            movingCountCore(threshold, rows, cols, i, j+1, flag) +
            movingCountCore(threshold, rows, cols, i, j-1, flag);
        return count;
    }
    private boolean sumLargerThanK(int i, int j, int threshold){
        return getSum(i)+getSum(j) > threshold;
    }
    private int getSum(int i){
        int sum=0;
        while(i>0){
            sum += i%10;
            i /= 10;  
        }
        return sum;
    }
}

你可能感兴趣的:(剑指offer,剑指offer)