摘要:日常生活中,很多事物都可以用樹來描述,例如書的目錄、工作單位的組織架構等等。樹是計算機中非常重要的一種數據結構,樹存儲方式可以提高數據的存儲、讀取效率。
本文分享自華爲雲社區《【雲駐共創】想了解二叉樹,看這篇文章就夠了》,作者: liuzhen007 。
前言
日常生活中,很多事物都可以用樹來描述,例如書的目錄、工作單位的組織架構等等。樹是計算機中非常重要的一種數據結構,樹存儲方式可以提高數據的存儲、讀取效率。
一、樹的基本定義
日常生活中,很多事物都可以用樹來描述,樹是計算機中非常重要的一種數據結構,樹存儲方式可以提高數據的存儲、讀取效率,比如利用二叉排序樹,既可以保證數據的檢索速度,同時,也可以保證數據的插入、刪除、修改的速度。
其實,樹的種類有很多種,比如普通的二叉樹、完全二叉樹、滿二叉樹、線索二叉樹、哈夫曼樹、二叉排序樹、平衡二叉樹、AVL平衡二叉樹、紅黑樹、B樹、B+樹、堆等。今天介紹的主要內容是二叉樹的基本知識和幾種基礎類型的二叉樹。
二、二叉樹的相關術語
在正式開講前,首先介紹一些關於二叉樹的專業名詞和術語,包括結點、結點的度、葉子結點、分支結點、結點的層次、樹的度、樹的深度等,瞭解這些基礎的專業名詞和術語對於我們理解二叉樹的特性有非常重要的幫助作用。
1)結點:包含一個數據元素及若干指向子樹分支的信息。
2)結點的度:一個結點擁有子樹的數據成爲結點的度。
3)葉子結點:也稱爲終端結點,沒有子樹的結點或者度爲零的結點。
4)分支結點:也稱爲非終端結點,度不爲零的結點成爲非終端結點。
5)結點的層次:從根結點開始,根結點的層次爲1,根的直接後繼層次爲2,以此類推。
6)樹的度:樹中所有結點的度的最大值。
7)樹的深度:樹中結點的最大層次。
1. 樹的特點
樹作爲一種特殊的數據結構,有非常多的特性,比如:
1)每個結點有多個或者零個子結點
2)沒有父結點的結點成爲根結點,沒有子結點的結點成爲葉結點
3)每一個非根結點只有一個父結點
4)每個結點及其後代結點整體上可以看做是一棵樹,稱爲當前結點爲根的子樹
2. 二叉樹的基本定義
1)二叉樹就是度不超過2的樹,其每個結點最多有兩個子結點
2)二叉樹的結點分爲左結點和右結點
3. 滿二叉樹
1)二叉樹的每一層的結點度都達到最大值,則這個二叉樹就是滿二叉樹
2)一棵深度爲n的滿二叉樹,有2^n-1個結點
4. 完全二叉樹
葉子結點只能出現在最下層和次下層,最後一層的葉子結點在左邊連續,倒數第二層的葉子結點在右邊連續,我們稱爲完全二叉樹。
三、二叉樹的創建
接下來,我們通過代碼來描述二叉樹,語言以Java爲例,其中結點類定義如下:
二叉查找樹類定義如下:
相關類定義好後,我們來看具體的方法實現,下面分別介紹。
1. size()方法
size()方法相對簡單,每次添加元素加一,刪除元素減一,維護一個公共的變量 num 即可,代碼實現如下:
2. put(Key key,Value value)方法
put(Key key,Value value)方法可以利用重載方法 put(Node x,Key key,Value value),因此實現也相對簡單,其中第一個參數只需要傳根結點即可,代碼實現如下:
3. put(Node x,Key key,Value value)方法
put(Node x,Key key,Value value)方法應該是整個類中實現相對較爲複雜的,下面進行簡單的分析。
第一種情況,當前樹中沒有任何一個結點,直接將新插入的結點作爲根結點。
第二種情況,當前樹不爲空,則從根結點開始。這種情況有可以細分爲三種情況:
1)如果新結點的key小於當前結點的key,則繼續查找當前結點的左子結點。
2)如果新結點的key大於當前結點的key,則繼續查找當前結點的右子結點。
3)如果新結點的key等於當前結點的key,則樹中已經存在這樣的結點,替換該結點的value值即可。
具體的代碼實現如下:
4. get(Key key)方法
get(Key key)方法和 put(Key key,Value value)方法類似,也可以利用重載方法 get(Node x,Key key)來實現,代碼實現如下:
5. get(Node x,Key key)方法
get(Node x,Key key)方法實現查詢方法從根結點開始,如果要查詢的key小於當前結點的key,則繼續找當前結點的左子結點;如果要查找的key大於當前結點的key,則繼續找當前結點的右子結點;如果要查找的key等於當前結點的key,則返回當前結點的value。
具體的代碼實現如下:
通過上面的代碼,我們可以看出 get()方法和 put()方法類似,都是通過遞歸調用實現的。
6. delete(Key key)方法
delete(Key key)方法也是一樣的思路,調用後面的重載方法就行了,代碼實現如下:
7. delete(Node x,Key key)方法
刪除方法的實現思路,以最複雜的情況爲例,首先找到被刪除的結點,找到被刪除結點右子樹中的最小結點 minNode,刪除右子樹中的最小結點,讓被刪除結點的左子樹成爲最小結點 minNode 的左子樹,讓被刪除結點右子樹成爲最小結點minNode的右子樹,讓被刪除結點的父結點指向最小結點 minNode。
具體的代碼實現如下:
既然代碼已經寫完了,接下來通過代碼來驗證一下,看看我們寫得是否正確:
答案輸出:
3
李四
2
Nice,太好了,通過上述輸出結果已經證明了程序是正確的。
四、查找二叉樹中最小和最大的鍵
比如二叉樹中用來記錄某個公司員工薪資和員工姓名數據,或者某班級學生們的排名和姓名數據。如何快速找出排名最高和最低的同學數據?
這樣的話,該怎麼做呢?其實,一般還可以整理出如下四個方法,定義如下:
1. min()方法
min()方法和上面提到的 get()和 put()方法類似,也可以使用下面的重載方法實現,具體代碼如下:
// 找出樹中最小的鍵
public key min() {
return min(root).key;
}
2. min(Node x)方法
min(Node x)方法需要根據傳入的結點位置,查找左子樹中的最小的結點,如果爲空,則直接返回空,具體代碼實現如下:
// 找出樹中最小鍵所在的結點
public Node min(Node x) {
if (x == null) {
return x;
}
Node minNode = x;
while (minNode.left != null) {
minNode = minNode.left;
}
return minNode;
}
3. max()方法
max()方法和上面提到的 min()方法類似,也可以使用下面的重載方法實現,具體代碼如下:
// 找出樹中最小的鍵
public key max() {
return max(root).key;
}
4. max(Node x)方法
max(Node x)方法需要根據傳入的結點位置,查找右子樹中的最大的結點,如果爲空,則直接返回空,具體代碼實現如下:
// 找出樹中最大鍵所在的結點
public Node min(Node x) {
if (x == null) {
return x;
}
Node maxNode = x;
while (maxNode.right != null) {
maxNode = maxNode.right;
}
return maxNode;
}
五、二叉樹的遍歷
二叉樹的遍歷有三種方式,分別是前序遍歷、中序遍歷、後序遍歷。
1. 前序遍歷
先訪問根結點,再訪問左子樹,最後訪問右子樹,比如下圖中的二叉樹,前序遍歷結果如下:
EBADCGFH。
2. 中序遍歷
先訪問左子樹,再訪問根結點,最後訪問右子樹,比如下圖中的二叉樹,中序遍歷結果如下:
ABCDEFGH。
3. 後序遍歷
先訪問左子樹,再訪問右子樹,最後訪問根結點,比如下圖中的二叉樹,後序遍歷結果如下:
ACDBFHGE。
結論
二叉樹的不僅在基礎的數據結構方面有非常重要的研究意義,在實際應用中也有非常重要的應用場景,兼顧了常規數據結構數組和鏈表的優點,同時又避免了二者天生的不足。許多實際的問題抽象出來的數據結構往往是二叉樹的形式,從而利用二叉樹的存儲結構和算法特性,因此學習二叉樹就非常的必要。希望通過今天本文的介紹能夠幫助大家深入理解和掌握二叉樹。