1. 程式人生 > 其它 >力扣 - 劍指 Offer 13. 機器人的運動範圍

力扣 - 劍指 Offer 13. 機器人的運動範圍

題目

劍指 Offer 13. 機器人的運動範圍

思路1(DFS)

  • 通過DFS遞迴,先往一個方向遞迴到最深地方,然後回溯,直到吧所有的條件都訪問一遍
  • 我們使用visited陣列記錄在訪問過程中被訪問的位置因為每個位置最多隻能訪問一次
  • 然後每次遞迴都要判斷是否滿足如下條件:
    1. 不超邊界,始終座標位置保證在範圍內
    2. 通過visited判斷是否被訪問過
    3. 判斷各個位數之和是否大於k
    4. 如果以上條件滿足其一,則結束遞迴(不能繼續往下走了,沒路可走。。)
  • 如果遍歷到頭,條件不滿足,就返回0
  • 然後回溯的時候帶上個遞迴的值加上當前的1(1是因為當前訪問了一次,如果沒有訪問到,就會直接返回0),向上傳遞,累加起來,即為總的可到達的格子數

程式碼

class Solution {

    // 將m、n、k定義為全域性變數
    int m, n, k;
    // 標記訪問過的位置
    boolean[][] visited;

    public int movingCount(int m, int n, int k) {
        this.m = m;
        this.n = n;
        this.k = k;
        // 初始化陣列,元素全部為false,訪問過的記為true
        visited = new boolean[m][n];
        // 開始搜尋
        int res = dfs(0, 0);

        return res;
    }

    public int dfs(int i, int j) {
        // 1. 判斷邊界
        // 2. 判斷是否被訪問過了
        if (i < 0 || i >= m || j < 0 || j >= n || visited[i][j]) {
            return 0;
        }

        // 判斷是否超過k
        if (help(i, j)) {
            return 0;
        }

        // 符合條件的做個標記,只能統計一次,再次訪問就無效了
        visited[i][j] = true;
        
        // 能訪問到這裡說明已經確認訪問到了一次了,因此在遞迴結束回溯的時候要加上當前的1作為返回值得以累加
        // 然後遞迴四個方向
        int res = 1 + dfs(i + 1, j) +
                      dfs(i - 1, j) +
                      dfs(i, j + 1) +
                      dfs(i, j - 1);

        return res;
    }

    // 判斷座標各位之和是否超過k
    public boolean help(int i, int j) {
        int count = 0;
        while (i != 0) {
            count += i % 10;
            i /= 10;
        }
        while (j != 0) {
            count += j % 10;
            j /= 10;
        }
        return count > k;
    }
}

複雜度分析

  • 時間複雜度:\(O(MN)\)M為行數。N為列數,最壞情況下要遍歷全部,即MN
  • 空間複雜度:\(O(MN)\)visited記錄訪問過的陣列,佔MN的空間

思路2(BFS)

  • 本題也可以使用BFS解決,和DFS有所不同的是:
    • DFS是先往一個方向遞迴到底部在回溯遞迴另一條路徑到底部,直到全部遍歷;
    • 而BFS是一層一層遍歷的(層序遍歷),使用佇列儲存,遍歷流程如下:將佇列的隊頭元素出隊,然後進行必要計算操作,最後將這個元素的第一層子元素加入到佇列末尾(本題是將下方和右方的子元素入隊),都讓,如果子元素不滿足某些條件(本題有一些條件限制),就不入隊。。。然後重複執行,直到佇列為空,說明遍歷結束
  • 和上一題一樣,也需要使用visited標記訪問過的元素

程式碼

class Solution {

    public int movingCount(int m, int n, int k) {
        int count = 0;
        boolean[][] visited = new boolean[m][n];
        Deque<int[]> queue = new LinkedList<>();
        queue.offer(new int[]{0, 0});

        while (!queue.isEmpty()) {
            // 從佇列中取出一個
            int[] cur = queue.poll();
            // 獲取兩個座標值
            int i = cur[0];
            int j = cur[1];

            // 判斷邊界
            // 判斷是否訪問過
            if (i < 0 || i >= m || j < 0 || j >= n || visited[i][j]) {
                continue;
            }
            // 判斷是否小於等於k
            if (help(i, j, k)) {
                continue;
            }

            // 標記訪問過
            visited[i][j] = true;
            // 訪問次數加1
            count++;
            
            // 將當前元素的下方和右方新增到佇列中
            queue.offer(new int[]{i+1, j});
            queue.offer(new int[]{i, j+1});
        }

        return count;
    }

    // 判斷座標各位之和是否超過k
    public boolean help(int i, int j, int k) {
        int count = 0;
        while (i != 0) {
            count += i % 10;
            i /= 10;
        }
        while (j != 0) {
            count += j % 10;
            j /= 10;
        }
        return count > k;
    }
}

複雜度分析

  • 時間複雜度:\(O(MN)\)M為行數。N為列數,最壞情況下要遍歷全部,即MN
  • 空間複雜度:\(O(MN)\),最壞情況下,佇列中儲存矩陣所有單元格索引,需要\(O(MN)\)的空間
我走得很慢,但我從不後退!