數據結構 - 二叉樹(Binary Tree)

1、二叉樹定義和特點

  • 1.1、定義
- 二叉樹(Binary Tree)是 **n**(n>=0)個結點的有限集合;
- 該集合或者爲空集(空二叉樹);
- 或者由一棵根節點和兩棵互不相交的、分別稱爲根節點的左子樹和右子樹的二叉樹組成
  • 1.2、特點
- 每個節點最多有2棵子樹(度最大爲2)
- 左子樹和右子樹是有順序的
- 即使某個節點只有一棵子樹,也要區分它是左子樹還是右子樹

2、二叉樹的遍歷

從根節點出發,按照某種次序依次訪問二叉樹中所有節點,使得每個節點被訪問一次且僅被訪問一次

  • 2.1、前序遍歷:先訪問根節點,然後前序遍歷左子樹,再前序遍歷右子樹
public void preOrderTraverse(Node<E> node) {
    if (node == null) return;
    
    System.out.println(node.element); // 打印
    preOrderTraverse(node.left);
    preOrderTraverse(node.right);
}
  • 2.2、中序遍歷:從根節點開始(⚠️並不先訪問根節點),中序遍歷根節點的左子樹,然後訪問根節點,最後中序遍歷右子樹。(⚠️二叉搜索樹的中序遍歷結果是升序或降序
public void inOrderTraver(Node<E> node) {
    if (node == null) return;

    inOrderTraver(node.left);
    System.out.println(node.element);
    inOrderTraver(node.right);
}
  • 2.3、後序遍歷:從左到右先葉子後結點的方式遍歷訪問左右子樹,最後是訪問根節點
public void postOrderTraverse(Node<E> node) {
    if (node == null) return;

    postOrderTraverse(node.left);
    postOrderTraverse(node.right);
    System.out.println(node.element);
}
  • 2.4、層序遍歷:從樹的第一層開始訪問(也就是根節點),從上而下逐層遍歷,在同一層中,按從左到右的順序對結點逐個訪問
public void levelOrderTraverse(){
    if (root == null) return;
    
    // 使用隊列來實現:先進先出 ✨✨✨✨✨
    Queue<Node<E>> queue = new LinkedList<>();
    queue.offer(root);

    while (!queue.isEmpty()) {
        Node<E> node = queue.poll();
        System.out.println(node.element);

        if (node.left != null) {
            queue.offer(node.left);
        }

        if (node.right != null) {
            queue.offer(node.right);
        }
    }
}
  • 2.4、根據遍歷結果重構二叉樹
以下結果可以保證重構出唯一一棵二叉樹✨✨✨
- 前序遍歷 + 中序遍歷
- 後序遍歷 + 中序遍歷


例:已知一棵二叉樹的前序遍歷序列爲:ABCDEF,中序遍歷序列爲:CBAEDF,請問這棵二叉樹的後續遍歷結果是多少?

思路:
- 根據:前序遍歷 + 中序遍歷 還原二叉樹,再查後續遍歷
- 前序遍歷,第一個結點一定是根結點✨✨✨

解題
- 前序遍歷:ABCDEF    =>     根節點爲A
- 中序遍歷:CBAEDF    =>     左子樹(CB)、右子樹(EDF),        即圖2.4.1
- 前序,先 B 後 C     =>     所以 C 是 B 的孩子(未確定左右)
- 中序,先 C 後 B     =>     C 是 B 的左孩子,               即圖2.4.2

同理,根據以上順序,得出右子樹

⚠️
- 如果不是特別熟練的話,一定要從還原出的樹中走一遍前、中、後,與題目是否相對
- 前序遍歷 + 後序遍歷 無法確定一個二叉樹,因爲不知道根節點,可能存在多種情況


3、特殊的二叉樹

  • 3.1、真二叉樹(Proper Binary Tree)

所有節點的度要麼爲0,要麼爲2

  • 3.2、滿二叉樹(Full Binary Tree)
  • 所有節點的度要麼爲0,要麼爲2,且所有的葉子節點都在最後一層
- 在同樣高度的二叉樹中,滿二叉樹的葉子節點數量最多,總節點數量最多
- 滿二叉樹一定是真二叉樹,真二叉樹不一定是滿二叉樹 ✨
  • 3.3、完全二叉樹(Complete Binary Tree)

葉子節點只會出現"最後2層",且"最後1層"的葉子節點都靠"左對齊"

- 完全二叉樹從根節點至倒數第2層是一棵滿二叉樹
- 滿二叉樹一定是完全二叉樹,完全二叉樹不一定是滿二叉樹

4、前驅節點、後繼節點

  • 4.1、前驅節點:中序遍歷時的前一個節點
⚠️如果是二叉搜索樹,前驅節點就是前一個比它小的節點

情況一:node.left != null
predecessor = node.left.right.right.right...
終止條件:right = null

情況二:node.left == null 
predecessor = node.parent.parent.parent.parent...
終止條件:node 在 parent的右子樹中

情況三:node.left == null && node.parent == null
沒有前驅



protected Node<E> predecessor(Node<E> node) {
    if (node == null) return node;

    // 前驅節點在左子樹當中(left.right.right.right...)
    Node<E> p = node.left;
    if (p.left != null) {
        while(p.right != null) {
            p = p.right;
        }
        return p;
    }

    // 從父節點,祖父節點尋找前驅節點
    while(node.parent != null && node == node.parent.left){
        node = node.parent;
    }

    return node.parent;
}
  • 4.2、後繼節點:中序遍歷時的後一個節點
⚠️- 如果是二叉搜索樹,前驅節點就是後一個比它大的節點

情況一:node.right != null 
successor = node.right.left.left.left...
終止條件:left 爲 null

情況二:node.right == null && node.parent != null
successor = node.parent.parent.parent.parent...
終止條件:node在parent左子樹中

情況三:node.right == null && node.parent == null
沒有後繼節點



protected Node<E> successor(Node<E> node) {
    if (node == null) return node;

    Node<E> p = node.right;
    if (p.right != null) {
        while(p.left != null) {
            p = p.left;
        }
        return p;
    }

    while(node.parent != null && node == node.parent.right){
        node = node.parent;
    }

    return node.parent;
}

5、其他重要性質

- 二叉樹的第i層,最多有2^(i-1)個節點(i>=1)✨

- 在高度爲h的二叉樹上最多有2^h - 1個節點(h>=1)✨

- 對於任何一棵二叉樹,如果葉子節點的個數爲n0,度爲2的節點個數爲n2,則有:n0 = n2 + 1;✨✨✨✨

驗證:
假設度爲1的節點個數爲n1,那麼二叉樹的節點總數 n=n0+n1+n2;✨✨
二叉樹的邊樹  T = n1 + 2*n2 = n-1 = n0 + n1 + n2 - 1;
消除等號左右共項:n0 = n2 + 1;

待補充:TODO()

6、工具篇

二叉樹-圖形化展示工具

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章