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()