一、BFS 知識點
BFS 就是廣度優先 (Breadth first search)。與之對應的還有深度優先 (Depth first search)。
網上找到一篇文章,比較通俗易懂的介紹了這 2 者。
今天重點是 BFS ,用它解決比如找到從起點 start 到終點 target 的最近距離問題(黑色起點、紅色終點)。
BFS 相對 DFS 的最主要的區別是:BFS 找到的路徑一定是最短的,但代價就是空間複雜度可能比 DFS 大很多 。
BFS 的核心思想就是把一些問題抽象成圖,從一個點開始,向四周開始擴散。
一般來說,我們寫 BFS 算法都是用隊列這種數據結構,每次將一個節點周圍的所有節點加入隊列。
這是框架,來自【labuladong】公衆號的學習資源。這裏要逐行閱讀理解,並記憶。
// 計算從起點 start 到終點 target 的最近距離
int BFS(Node start, Node target) {
Queue<Node> q; // 核心數據結構
Set<Node> visited; // 避免走回頭路
q.offer(start); // 將起點加入隊列
visited.add(start);
int step = 0; // 記錄擴散的步數
while (q not empty) {
int sz = q.size();
/* 將當前隊列中的所有節點向四周擴散 */
for (int i = 0; i < sz; i++) {
Node cur = q.poll();
/* 劃重點:這裏判斷是否到達終點 */
if (cur is target)
return step;
/* 將 cur 的相鄰節點加入隊列 */
for (Node x : cur.adj())
if (x not in visited) {
q.offer(x);
visited.add(x);
}
}
/* 劃重點:更新步數在這裏 */
step++;
}
}
代碼裏的一些方法說明:
q.offer(start)
,往隊列尾部插入元素,當超出隊列界限的時候,返回 false 。q.poll()
,獲取並刪除Queue中的第一個元素。visited.add(start)
,向 set 集合裏添加元素。- 隊列 q 是 BFS 的核心數據結構。
cur.adj()
泛指 cur 相鄰的節點,比如說二維數組中,cur 上下左右四面的位置就是相鄰節點。visited
的主要作用是防止走回頭路,大部分時候都是必須的。但是像一般的二叉樹結構,沒有子節點到父節點的指針,不會走回頭路就不需要 visited。
二、BFS 解決二叉樹的最小深度
給定一個二叉樹,找出其最小深度。
最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。
說明:葉子節點是指沒有子節點的節點。
之前的做法:
class Solution {
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
// 左右孩子都爲null,返回 1
if (root.left == null && root.right == null) {
return 1;
}
int leftHeight = minDepth(root.left);
int rightHeight = minDepth(root.right);
// 左右孩子有一個爲null
if (leftHeight == 0 || rightHeight == 0) {
return leftHeight + rightHeight + 1;
}
// 左右孩子都不爲null,返回比較最短的 + 1
return Math.min(leftHeight, rightHeight) + 1;
}
}
使用 BFS ,套用框架:
- 起點就是根節點
- 終點就是最靠近根節點的那個「葉子節點」
- 葉子節點就是兩個子節點都是 null 的節點
class Solution {
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root); // 將起點加入隊列
int depth = 1; // root 本身就是一層,depth 初始化爲 1
while (!q.isEmpty()) {
// 將當前隊列中的所有節點向四周擴散
int sz = q.size();
for (int i = 0; i < sz; i++) {
TreeNode cur = q.poll(); // 獲取並刪除Queue中的第一個元素
/* 劃重點:這裏判斷是否到達終點 */
if (cur.left == null && cur.right == null) {
return depth;
}
/* 將 cur 的相鄰節點加入隊列 */
if (cur.left != null) {
q.offer(cur.left);
}
if (cur.right != null) {
q.offer(cur.right);
}
}
/* 這裏增加步數 */
depth++;
}
return depth;
}
}