樹Tree的基本概念
- 節點、根節點、父節點、子節點、兄弟節點
- 一棵樹可以沒有任何節點,稱為
空樹
- 一棵樹可以只有一個節點,也就是只有根節點
- 子樹、左子樹、右子樹
節點的
度
(degree):子樹的個數
??:1
的度是5
個:2、3、4、5、6
??:2
的度是2
個:21
、22
??:61
的度是0
個樹的
度
(degree):所有節點度中的最大值
??:圖中最大的是節點1
,有5
個度葉子
的節點(leaf):度為0
的節點
??:圖中21、221、222、223、31、5、51、52、61
都是葉子節點非葉子
的節點:度不為0
的節點
樹的
層數
(level):根節點在第1
層,根節點的子節點在第2
層,以此類推(有些教程從第0
層計算)節點的
深度
(depth):從根節點到當前節點的唯一路勁上的節點總數
??:節點2
的深度:從根節點1
到2
經過了2
個節點,深度為2
??:節點31
的深度:從根節點1
到31
經過了3
個節點,深度為3
節點的
高度
(height):從當前節點到最遠葉子節點的路勁上的節點總數
??:節點2
的高度:圖中看出從節點2
到最遠的葉子節點為221、222、223
中的一個,取其中一個計算,經歷了2-22-221
總共3
個節點,所有高度為3
樹的
深度
:所有節點深度中的最大值樹的
高度
:所有節點高度中的最大值樹的
深度
等于 樹的高度
有序樹、無序樹、森林
- 有序樹:樹中的任意節點的子節點之間有順序關系
- 有序樹:樹中的任意節點的子節點之間沒有順序關系(也稱為“自由樹”)
- 森林:由
m (m >= 0)
棵互不相交的樹組成的集合
二叉樹Binary Tree
二叉樹的特點:
- 每個節點的度最大值為
2
(最多擁有2
棵子樹) - 左子樹跟右子樹是有順序的
- 即使某個節點只有一棵子樹,也要區分左右子樹
- 二叉樹是有序樹
二叉樹的性質:
- 非空二叉樹的第
i
層,最多有 2i-1 個節點(i >= 1
) - 高度為
h
的二叉樹上最多有 2h-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
真二叉樹Proper Binary Tree
- 所有節點的
度
要么是0
,要么是2
滿二叉樹Full Binary Tree
- 所有節點的
度
要么是0
,要么是2
,且所有的葉子節點都在最后一層 - 在同樣高度的二叉樹中,滿二叉樹的葉子節點數量最多,總節點數量最多
- 滿二叉樹一定是真二叉樹,真二叉樹不一定是滿二叉樹
- 假設滿二叉樹的高度為
h(h>= 1)
,那么:
??:第i
層的節點數量:2i-1
??:葉子節點數量:2h-1
??:總節點的數量n:n = 2h-1 = 20 + 21 + 22 + ... 2h-1
完全二叉樹Complete Binary Tree
- 葉子節點只會出現在最后
2
層,且最后1
層的葉子節點都靠左
對齊 - 完全二叉樹從
根節點
至倒數第2
層是一棵滿二叉樹
- 滿二叉樹一定是完全二叉樹,完全二叉樹不一定是滿二叉樹
- 度為
1
的節點只有左子樹 - 度為
1
的節點要么是1
個,要么是0
個 - 同樣節點數量的二叉樹,完全二叉樹的高度最小
- 假設完全二叉樹的高度為
h(h>= 1)
,那么:
??:至少有2h-1個節點 20 + 21 + 22 + ... 2h-2 + 1
??:最多有2h-1個節點 20 + 21 + 22 + ... 2h-1,滿二叉樹
??:總節點數量:2h-1<=n
<=2h,取對數:h - 1 <=< h
二叉搜索樹Binary Search Tree
1、二叉搜索樹是二叉樹的一種,是應用非常廣泛的一種二叉樹,簡稱BST
- 又稱為:二叉查找樹、二叉排序樹
- 任意一個節點的值都
大于
其左子樹
所有節點的值 - 任意一個節點的值都
小于
其右子樹
所有節點的值 - 它的左右子樹也是一顆二叉搜索樹
2、二叉搜索樹可以大大提高搜索數據的效率
3、二叉搜索樹存儲的元素必須具備可比較性
- 比如
int
、double
等 - 如果自定義類型,需要制定比較方式
- 不允許為
null
二叉樹的遍歷
- 遍歷是數據結構中常見操作:
1??:把所有元素都訪問一遍 - 線性數據結構的遍歷比較簡單:
1??:正序遍歷
2??:逆序遍歷 - 根據節點訪問順序的不同,二叉樹的常見遍歷方式有4種:
1??:前序遍歷
2??:中序遍歷
3??:后序遍歷
4??:層序遍歷
二叉樹的遍歷 —— 前序遍歷
- 訪問順序:
??:根節點
— 前序遍歷左子樹
— 前序遍歷右子樹
??:7
——4、2、1、3、5
——9、8、11、10、12
可以利用遞歸來實
public void preporderTraversal() {
preporderTraversal(rootNode);
}
private void preporderTraversal(Node<E> node) {
if (node == null) { return; }
System.out.println(node.element);
preporderTraversal(node.leftNode);
preporderTraversal(node.rightNode);
}
二叉樹的遍歷 —— 中序遍歷
- 訪問順序:
??:中序遍歷左子樹
—根節點
— 中序遍歷右子樹
??:1、2、3、4、5
——7
——8、9、10、11、12
- 如果訪問順序是這樣的:
??:中序遍歷右子樹
—根節點
— 中序遍歷左子樹
??:12、11、10、9、8
——7
——5、4、3、2、1
-
二叉搜索樹
的中序遍歷結果是升序或者降序的
public void inorderTraversal() {
inorderTraversal(rootNode);
}
private void inorderTraversal(Node<E> node) {
if (node == null) { return; }
inorderTraversal(node.leftNode);
System.out.println(node.element);
inorderTraversal(node.rightNode);
}
二叉樹的遍歷 —— 后序遍歷
- 訪問順序:
??:后序遍歷左子樹
— 后序遍歷右子樹
—根節點
??:1、3、2、5、4
——8、10、12、11、9
——7
public void postorderTraversal() {
postorderTraversal(rootNode);
}
private void postorderTraversal(Node<E> node) {
if (node == null) { return; }
postorderTraversal(node.leftNode);
postorderTraversal(node.rightNode);
System.out.println(node.element);
}
二叉樹的遍歷 —— 層序遍歷
- 訪問順序:
??:從上到下,從左到右依次訪問每一個節點
??:7
——4、9
——2、5、8、1
——1、3、10、12
- 實現思路:使用隊列
1??:將根節點入隊
2??:循環執行下面操作,直到隊列為空:
??:將隊頭節點A出隊,進行訪問
??:將A的左子節點入隊
??:將A的右子節點入隊
public void levelOrderTraversal() {
if (rootNode == null) { return; }
Queue<Node<E>> queue = new LinkedList<>();
queue.offer(rootNode);
while (!queue.isEmpty()) {
Node<E> node = queue.poll();
System.out.println(node.element);
//是否有左右子節點
if (node.leftNode != null) {
queue.offer(node.leftNode);
}
if (node.rightNode != null) {
queue.offer(node.rightNode);
}
}
}