力扣 - 劍指 Offer 13. 機器人的運動範圍
阿新 • • 發佈:2021-11-21
題目
思路1(DFS)
- 通過DFS遞迴,先往一個方向遞迴到最深地方,然後回溯,直到吧所有的條件都訪問一遍
- 我們使用
visited
陣列記錄在訪問過程中被訪問的位置(因為每個位置最多隻能訪問一次) - 然後每次遞迴都要判斷是否滿足如下條件:
- 不超邊界,始終座標位置保證在範圍內
- 通過
visited
判斷是否被訪問過 - 判斷各個位數之和是否大於
k
- 如果以上條件滿足其一,則結束遞迴(不能繼續往下走了,沒路可走。。)
- 如果遍歷到頭,條件不滿足,就返回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)\)的空間