Java TreeMap 用紅黑樹不用 AVL Tree,是有原因的。

AVL Tree 哪裡不夠好?

AVL Tree 要求左右子樹高度差 ≤ 1,非常嚴格。代價是插入或刪除一個節點後,可能要做很多次旋轉才能修復平衡。

讀多寫少的場景沒問題,但 Map 之類的資料結構增刪查都很頻繁——AVL 的修復成本太高。

紅黑樹的思路是:放寬平衡標準,允許「差不多平衡」,換來插入/刪除最多只需 2–3 次旋轉

五大性質

1. 每個節點是紅色或黑色
2. 根節點是黑色
3. 所有葉(NIL)是黑色
4. 紅色節點的子節點必須是黑色(不能連續兩個紅)
5. 從任一節點到其所有葉節點的路徑,黑色節點數相同

性質 4 + 5 合起來保證:最長路徑 ≤ 最短路徑 × 2,樹高不超過 2 log n。

黑高 = 2 的紅黑樹:

         10(B)
        /     \
      5(R)    20(R)
      / \     / \
    3(B) 7(B) 15(B) 25(B)

最短路徑:10→5→3  長度 2(全黑)
最長路徑:10→5(R)→3  長度 2,黑高相等 ✓

插入:三種修復情況

新節點一律塗紅色(不影響黑高)。如果父節點也是紅色,違反性質 4,開始修復。

修復看叔叔節點的顏色:

情況一:叔叔是紅色 → 重新著色

    G(B)              G(R) ← 繼續向上
   /    \            /    \
P(R)   U(R)  →   P(B)   U(B)
/                 /
Z(R)            Z(R)

父和叔變黑、祖父變紅,黑高不變,問題往上推。

情況二:叔叔黑 + 內側子 → 先旋轉,轉成情況三

  G(B)          G(B)
 /    \         /    \
P(R)  U(B) → Z(R)  U(B)
  \           /
  Z(R)       P(R)
對 P 左旋

情況三:叔叔黑 + 外側子 → 旋轉 + 變色,修復完成

    G(B)            P(B)
   /    \          /    \
P(R)   U(B)  →  Z(R)  G(R)
/                         \
Z(R)                     U(B)
對 G 右旋,P 變黑,G 變紅
private void insertFixUp(Node z) {
    while (z.parent != null && z.parent.color == RED) {
        if (z.parent == z.parent.parent.left) {
            Node uncle = z.parent.parent.right;
            if (uncle.color == RED) {           // 情況一
                z.parent.color = BLACK;
                uncle.color = BLACK;
                z.parent.parent.color = RED;
                z = z.parent.parent;
            } else {
                if (z == z.parent.right) {      // 情況二 → 情況三
                    z = z.parent;
                    leftRotate(z);
                }
                z.parent.color = BLACK;         // 情況三
                z.parent.parent.color = RED;
                rightRotate(z.parent.parent);
            }
        }
        // 右側為鏡像邏輯...
    }
    root.color = BLACK;
}

刪除

刪除黑色節點時黑高被破壞。修復靠「雙重黑色」概念——把「少了一個黑色」掛在替代節點上往上傳遞。4 種情況(各有鏡像版),最多 3 次旋轉解決。

AVL vs Red-Black Tree

AVLRed-Black
平衡嚴格度嚴格(BF ≤ 1)寬鬆(黑高相等)
查詢略快略慢
插入/刪除旋轉較多最多 2–3 次
適用讀多寫少讀寫均衡
現實部分 DB 索引Java TreeMap、Linux scheduler、Nginx

面試常見:「為什麼 Java TreeMap 用 Red-Black Tree?」——因為 Map 增刪查都頻繁,Red-Black Tree 修改成本更低。


紅黑樹不追求完美平衡,只追求「夠平衡」——工程上通常這樣更快。