
https://labuladong.online/algo/data-structure-basic/binary-tree-traverse-basic/#层序遍历-bfs
今天是學習的第 21 天,主要學習了二叉樹的層序遍歷(BFS):
| 特性 | DFS(深度優先) | BFS(廣度優先) | 
|---|---|---|
| 遍歷方式 | 一列一列,從左到右垂直遍歷 | 一層一層,從上到下水平遍歷 | 
| 實現方式 | 函式遞歸調用(利用調用堆棧) | 隊列(Queue)資料結構 | 
| 適用場景 | 樹的深度相關問題 | 最短路徑、層級相關問題 | 
二叉樹的層序遍歷,就是一層一層地遍歷二叉樹:
       1          ← 第 1 層
      / \
     2   3        ← 第 2 層
    / \   \
   4   5   6      ← 第 3 層
層序遍歷結果: 1 → 2 → 3 → 4 → 5 → 6
第一種寫法較簡單,就是利用隊列的概念,每次將隊頭元素取出來,然後把它的左右子節點加入隊列。
但缺點是無法知道當前節點所在的層數,而知道節點的層數是一個常見的需求。
class TreeNode {
    constructor(val) {
        this.val = val;
        this.left = null;
        this.right = null;
    }
}
var levelOrderTraverse = function(root) {
    if (root === null) {
        return;
    }
    var q = [];
    q.push(root);
    while (q.length !== 0) {
        var cur = q.shift();
        // 訪問 cur 節點
        console.log(cur.val);
        // 把 cur 的左右子節點加入隊列
        if (cur.left !== null) {
            q.push(cur.left);
        }
        if (cur.right !== null) {
            q.push(cur.right);
        }
    }
}
// 範例:建立一棵滿二叉樹(高度為 3)
//        1
//      /   \
//     2     3
//    / \   / \
//   4   5 6   7
const root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.left = new TreeNode(6);
root.right.right = new TreeNode(7);
levelOrderTraverse(root);
// 執行順序是 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7
寫法二是將寫法一做了一些調整,讓我們能夠知道當前節點所在的層數,也是比較常用的寫法。
class TreeNode {
    constructor(val) {
        this.val = val;
        this.left = null;
        this.right = null;
    }
}
var levelOrderTraverse = function(root) {
    if (root === null) {
        return;
    }
    let q = [];
    q.push(root);
    // 記錄當前遍歷到的層數(根節點是為第 1 層)
    let depth = 1;
    while (q.length !== 0) {
        let sz = q.length;
        for (let i = 0; i < sz; i++) {
            let cur = q.shift();
            // 訪問 cur 節點,同時知道它所在的層數
            console.log("depth = " + depth + ", val = " + cur.val);
            // 把 cur 的左右子節點加入隊列
            if (cur.left !== null) {
                q.push(cur.left);
            }
            if (cur.right !== null) {
                q.push(cur.right);
            }
        }
        depth++;
    }
};
// 範例:建立一棵滿二叉樹(高度為 3)
//        1
//      /   \
//     2     3
//    / \   / \
//   4   5 6   7
const root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.left = new TreeNode(6);
root.right.right = new TreeNode(7);
levelOrderTraverse(root);
// depth = 1, val = 1
// depth = 2, val = 2
// depth = 2, val = 3
// depth = 3, val = 4
// depth = 3, val = 5
// depth = 3, val = 6
// depth = 3, val = 7
寫法三是在寫法一的基礎上添加了一個 State 類,讓每個節點自己負責維護自己的路徑權重和。
class TreeNode {
    constructor(val) {
        this.val = val;
        this.left = null;
        this.right = null;
    }
}
function State(node, depth) {
    this.node = node;
    this.depth = depth;
}
var levelOrderTraverse = function(root) {
    if (root === null) {
        return;
    }
    // @visualize bfs
    var q = [];
    // 根節點的路徑權重和是 1
    q.push(new State(root, 1));
    while (q.length !== 0) {
        var cur = q.shift();
        // 訪問 cur 節點,同時知道它的路徑權重和
        console.log("depth = " + cur.depth + ", val = " + cur.node.val);
        // 把 cur 的左右子節點加入隊列
        if (cur.node.left !== null) {
            q.push(new State(cur.node.left, cur.depth + 1));
        }
        if (cur.node.right !== null) {
            q.push(new State(cur.node.right, cur.depth + 1));
        }
    }
};
// 範例:建立一棵滿二叉樹(高度為 3)
//        1
//      /   \
//     2     3
//    / \   / \
//   4   5 6   7
const root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.left = new TreeNode(6);
root.right.right = new TreeNode(7);
levelOrderTraverse(root);
// depth = 1, val = 1
// depth = 2, val = 2
// depth = 2, val = 3
// depth = 3, val = 4
// depth = 3, val = 5
// depth = 3, val = 6
// depth = 3, val = 7