zl程序教程

您现在的位置是:首页 >  IT要闻

当前栏目

面试题13. 机器人的运动范围

2023-02-18 16:35:25 时间

题目:

 

 

 

思路:

【1】首先明确限制:

1.是从【0,0】开始移动
2.只能上下左右移动【但不能超出边界】
3.不能踏入坐标的位数之后比K大的位置

【2】思路一:考虑广度搜索,通过用队列存储下一个可达的地址,然后再队列中拿出那些地址的下一个可达地址。其中只要用数组表示那些地址已经走过了即可【保证不重复走同一个位置】。但是需要额外开辟一个数组用于存储数据,而且还要对坐标构建一个结构,这些都需要花费一定的空间。好处是一个循环便能解决。

【3】思路二:考虑深度搜索,利用递归的方式实现,通过传入坐标为参数,可以避免对坐标构建,同时也避免了开辟队列的内存空间,但是需要对调用栈不断加深【这也是需要空间的,好处是调用完会释放】,通过leetcode运行结果,这种明显花费时间更少,所用空间也更少,估计是调用栈不深的缘故吧

代码展示:

//面试题13. 机器人的运动范围
public class Offer {
    public static void main(String[] args) {
        Random random = new Random();
        int n = random.nextInt(100)+1;
        int m = random.nextInt(100)+1;
        int k = random.nextInt(20)+1;

        System.out.println(Method1(m,n,k));
    }

    // 思路一:BFS(广度优先搜索),通过队列,把当前位置能够达到的下一个位置添加的队列里面
    // 从而达到以点扩大到面的程度
    public static int Method1(int m, int n, int k){
        //临时变量visited记录格子是否被访问过
        boolean[][] visited = new boolean[m][n];
        int res = 0;
        //创建一个队列,保存的是访问到的格子坐标,是个二维数组
        Queue<int[]> queue = new LinkedList<>();
        //从左上角坐标[0,0]点开始访问,add方法表示把坐标
        // 点加入到队列的队尾
        queue.add(new int[]{0, 0});
        while (queue.size() > 0) {
            //这里的poll()函数表示的是移除队列头部元素,因为队列
            // 是先进先出,从尾部添加,从头部移除
            int[] x = queue.poll();
            int i = x[0], j = x[1];
            //i >= m || j >= n是边界条件的判断,k < sum(i, j)判断当前格子坐标是否
            // 满足条件,visited[i][j]判断这个格子是否被访问过
            if (i >= m || j >= n || k < sum(i, j) || visited[i][j]){
                continue;
            }
            //标注这个格子被访问过
            visited[i][j] = true;
            res++;
            //把当前格子下边格子的坐标加入到队列中
            queue.add(new int[]{i + 1, j});
            //把当前格子右边格子的坐标加入到队列中
            queue.add(new int[]{i, j + 1});
        }
        return res;
    }

    //思路二:DFS(深度优先搜索),通过利用递归,先走到最底层,先遍历完一条线再遍历其他线
    public static int Method2(int m, int n, int k){
        //临时变量visited记录格子是否被访问过
        boolean[][] visited = new boolean[m][n];
        return dfs(0, 0, m, n, k, visited);
    }

    public static int dfs(int i, int j, int m, int n, int k, boolean[][] visited) {
        // i >= m || j >= n是边界条件的判断,
        // k < sum(i, j)判断当前格子坐标是否满足条件,
        // visited[i][j]判断这个格子是否被访问过
        if (i >= m || j >= n || k < sum(i, j) || visited[i][j]){
            return 0;
        }
        //标注这个格子被访问过
        visited[i][j] = true;
        // 沿着当前格子的右边和下边继续访问,
        // 如果画图的话会明显的发现只要每个点都沿着右边和下边遍历,就会遍历到全部
        return 1 + dfs(i + 1, j, m, n, k, visited) + dfs(i, j + 1, m, n, k, visited);
    }

    //计算两个坐标数字的和
    private static int sum(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;
    }
}