二元樹(Binary Tree)是一種樹形資料結構,由節點構成,每個節點最多有兩個子節點:一個左子節點和一個右子節點。
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) { this.val = val; }
}
"二元樹"(Binary Tree)這個名稱的由來是因為二元樹的每個節點最多有兩個子節點,一個左子節點和一個右子節點。其中,「二叉」指的是兩個,因此「二元樹」表示每個節點最多可以分支成兩個子節點。基本定義:
相關基本概念:
在二元樹基本定義上,加上一些規則,可以衍生出更多種類的二元樹。比如:
二元搜尋樹(Binary Search Tree,BST): 一種特殊的二元樹,滿足以下性質:對於樹中的每個節點,其左子樹中的值都小於該節點的值,而其右子樹中的值都大於該節點的值。BST通常用於實現有序資料集合。
完全二元樹(Complete Binary Tree): 一個二元樹,其所有層次(深度)除了最後一層外,都是完全填充的,且最後一層的節點從左到右填充,沒有空隙。
平衡二元樹(Balanced Binary Tree): 一種高度平衡的二元樹,其中每個節點的兩棵子樹的高度差不超過1。平衡二元樹通常用於提高查詢、插入和刪除操作的效能。
下一部分要寫的是二元樹基本遍歷程式碼實現其實可以有多種,思量後用遞迴實現應該是初接觸者比較簡潔好理解的方式。為此,在寫二元樹下一部分內容之前簡單寫下基礎遞迴演演算法,以保證本系列文章承前啟後。
遞迴(Recursion),在數學與電腦科學中對其描述的說法有很多,比如:
當然本文非學術著作"哪種描述比較合適"在此不多做分析,從編碼實踐的角度第一種說法更為地氣一點。
「將問題分解為同類的子問題」 這一點是用遞迴的方式來解題的關鍵,這裡用個簡單的累加和的例子:
設計一個函數,輸入引數為 n ,返回 1+2+...n 的和。
public int sum(int n){
int result = 0;
for (int i = 1; i <= n ; i++) {
result = result + i;
}
return result;
}
想想初學 C 語言for迴圈的時候應該都有寫過上述程式碼,從 1開始遞增加到 n 這其實是典型的遞推。那如果用遞迴的思路來思考的話:
求 1+2+..n 的和(問題) -> 就是 n 加上 求 1+2+..(n-1) 的和(同類的子問題);
其中最基本的情況 1 的和 為 1。
public int sum( int n ) {
if( 1 == n) return 1;
return n + sum(n-1);
}
可以看到遞迴的程式碼實現上是不是非常簡潔。大部分初學者思考上比較習慣於遞推,如果第一次接觸遞迴角度思考會有些不適應(或者無法獨立分析出來遞迴)也是正常。當慢慢熟悉後,會發現用遞迴的思路解決某些演演算法問題往往會非常簡單(在本篇接下來的內容中就能發現這點)。
在 初學遞迴 過度到 熟悉遞迴 這個階段,筆者建議可以考慮把一些用遞推已經解決了的問題 用 遞迴的思路嘗試解決,習慣遞迴思路後會開啟一片新世界。
二元樹的遍歷是指按照一定的順序存取二元樹的所有節點。在二元樹中,有三種常見的遍歷方式,它們分別是前序遍歷、中序遍歷和後序遍歷。
從根節點開始,首先存取根節點,然後按照前序遍歷的方式依次存取左子樹和右子樹。前序遍歷通常用於複製一棵樹或計算表示式的值。
存取順序:根節點 -> 左子樹 -> 右子樹
給你二元樹的根節點 root ,返回它節點值的 前序 遍歷。
從根節點開始,首先按照中序遍歷的方式存取左子樹,然後存取根節點,最後存取右子樹。中序遍歷通常用於存取二元搜尋樹中的節點,以升序或降序存取節點值。
存取順序:左子樹 -> 根節點 -> 右子樹
給定一個二元樹的根節點 root ,返回 它的 中序 遍歷 。
針對後序遍歷(Postorder Traversal)從根節點開始,首先按照後序遍歷的方式存取左子樹,然後存取右子樹,最後存取根節點。後序遍歷通常用於釋放二元樹的記憶體,或計算表示式的值。存取順序:左子樹 -> 右子樹 -> 根節點,在此不過多描述相信一定能夠完成編碼。
給定兩個整數陣列 preorder 和 inorder ,其中 preorder 是二元樹的先序遍歷, inorder 是同一棵樹的中序遍歷,請構造二元樹並返回其根節點。
本系列文章中已經介紹了連結串列、遞迴、二元樹,解決演演算法問題往往會需要綜合應用。不妨來看下下面這個問題:
給你二元樹的根結點 root ,請你將它展開為一個單連結串列:
展開後的單連結串列應該同樣使用 TreeNode ,其中 right 子指標指向連結串列中下一個結點,而左子指標始終為 null 。
展開後的單連結串列應該與二元樹 先序遍歷 順序相同。