what's the 樹
在了解二叉樹之前,首先我們得有樹的概念。
樹是一種數據結構又可稱為樹狀圖,如文檔的目錄、HTML的文檔樹都是樹結構,它是由n(n>=1)個有限節點組成一個具有層次關系的集合。把它叫做“樹”是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。它具有以下的特點:
-
- 每個節點有零個或多個子節點;
-
- 沒有父節點的節點稱為根節點;
-
- 每一個非根節點有且只有一個父節點;
-
- 除了根節點外,每個子節點可以分為多個不相交的子樹;
有關樹的一些相關術語:
-
- 節點的度:一個節點含有的子樹的個數稱為該節點的度;
-
- 葉節點或終端節點:度為0的節點稱為葉節點;
-
- 非終端節點或分支節點:度不為0的節點;
-
- 雙親節點或父節點:若一個節點含有子節點,則這個節點稱為其子節點的父節點;
-
- 孩子節點或子節點:一個節點含有的子樹的根節點稱為該節點的子節點;
-
- 兄弟節點:具有相同父節點的節點互稱為兄弟節點;
-
- 樹的度:一棵樹中,最大的節點的度稱為樹的度;
-
- 節點的層次:從根開始定義起,根為第1層,根的子節點為第2層,以此類推;
-
- 樹的高度或深度:樹中節點的最大層次;
-
- 堂兄弟節點:雙親在同一層的節點互為堂兄弟;
-
- 節點的祖先:從根到該節點所經分支上的所有節點;
-
- 森林:由m(m>=0)棵互不相交的樹的集合稱為森林;
樹的種類有:無序樹、有序樹、二叉樹、霍夫曼樹。其中最重要應用最多的就是二叉樹,下面我們來學習有關二叉樹的知識。
二叉樹
二叉樹的定義為度不超過2的樹,即每個節點最多有兩個叉(兩個分支)。上面那個例圖其實就是一顆二叉樹。
二叉樹有兩個特殊的形態:滿二叉樹和完全二叉樹
滿二叉樹
一個二叉樹,如果除了葉子節點外每一個層的結點數都達到最大值,則這個二叉樹就是滿二叉樹。
完全二叉樹
葉節點只能出現在最下層和次下層,並且最下面一層的結點都集中在該層最左邊的若干位置的二叉樹為完全二叉樹。即右邊的最下層和次下層可以適當缺一個右子數
完全二叉樹是效率很高的數據結構
二叉樹的遍歷
二叉樹的鏈式存儲:將二叉樹的節點定義為一個對象,節點之間通過類似鏈表的鏈接方式來連接。
二叉樹結點的定義
#二叉樹結點的定義 class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None self.rchild = None
二叉樹的遍歷分為四種——前序遍歷、中序遍歷、后序遍歷和層級遍歷
設樹結構為:
- 前序遍歷:先打印根,再遞歸其左子樹,后遞歸其右子數 E ACBD GF
- 中序遍歷:以根為中心,左邊打印左子樹,右邊打印右子樹(注意,每個子樹也有相應的根和子樹) A BCD E GF
- 后序遍歷:先遞歸左子樹,再遞歸右子樹,后打印根(注意,每個子樹也有相應的根和子樹BDC A FG E
- 層次遍歷:從根開始一層一層來,同一層的從左到右輸出E AG CF BD
四種遍歷方法的代碼實現:

from collections import deque #結點的定義 class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None self.rchild = None #二叉樹結點 a = BiTreeNode('A') b = BiTreeNode('B') c = BiTreeNode('C') d = BiTreeNode('D') e = BiTreeNode('E') f = BiTreeNode('F') g = BiTreeNode('G') #結點之間的關系 e.lchild = a e.rchild = g a.rchild = c c.lchild = b c.rchild = d g.rchild = f root = e #前序遍歷:先打印根,再遞歸左孩子,后遞歸右孩子 def pre_order(root): if root: print(root.data, end='') pre_order(root.lchild) pre_order(root.rchild) #中序遍歷:以根為中心,左邊打印左子樹,右邊打印右子樹(注意,每個子樹也有相應的根和子樹) #(ACBD) E (GF)-->(A(CBD)) E (GF)-->(A (B C D)) E (G F) def in_order(root): if root: in_order(root.lchild) print(root.data, end='') in_order(root.rchild) #后序遍歷:先遞歸左子樹,再遞歸右子數,后打印根(注意,每個子樹也有相應的根和子樹) # (ABCD)(GF)E-->((BCD)A)(GF)E-->(BDCA)(FG)E def post_order(root): if root: post_order(root.lchild) post_order(root.rchild) print(root.data, end='') #層次遍歷:一層一層來,同一層的從左到右輸出 def level_order(root): queue = deque() queue.append(root) while len(queue) > 0: node = queue.popleft() print(node.data,end='') if node.lchild: queue.append(node.lchild) if node.rchild: queue.append(node.rchild) pre_order(root)#EACBDGF print("") in_order(root)#ABCDEGF print("") post_order(root)#BDCAFGE print("") level_order(root)#EAGCFBD
二叉搜索樹
二叉搜索樹(Binary Search Tree),它或者是一棵空樹,或者是具有下列性質的二叉樹: 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; 它的左、右子樹也分別為二叉搜索樹。
二叉搜索樹一個很好玩的網址,集成了增刪改的功能:https://visualgo.net/en/bst
二叉搜索樹的中序遍歷得到的是原來列表按升序排序的列表
由列表生成二叉搜索樹、通過二叉搜索樹查詢值和刪除值的示例代碼:

#結點定義 class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None self.rchild = None #建立二叉搜索樹(循環列表,插入值) class BST: def __init__(self, li=None): self.root = None if li: self.root = self.insert(self.root, li[0])#列表的第一個元素是根 for val in li[1:]: self.insert(self.root, val) #生成二叉搜索樹遞歸版本 def insert(self, root, val): if root is None: root = BiTreeNode(val) elif val < root.data:#插入的值小於root,要放到左子樹中(遞歸查詢插入的位置) root.lchild = self.insert(root.lchild, val) else:#插入的值大於root,要放到右子樹中(遞歸查詢插入的位置) root.rchild = self.insert(root.rchild, val) return root #生成二叉搜索樹不遞歸的版本 def insert_no_rec(self, val): p = self.root if not p: self.root = BiTreeNode(val) return while True: if val < p.data: if p.lchild: p = p.lchild else: p.lchild = BiTreeNode(val) break else: if p.rchild: p = p.rchild else: p.rchild = BiTreeNode(val) break #查詢遞歸版本 def query(self, root, val): if not root: return False if root.data == val: return True elif root.data > val: return self.query(root.lchild, val) else: return self.query(root.rchild, val) #查詢非遞歸版本 def query_no_rec(self, val): p = self.root while p: if p.data == val: return True elif p.data > val: p = p.lchild else: p = p.rchild return False #中序遍歷,得到的是升序的列表 def in_order(self, root): if root: self.in_order(root.lchild) print(root.data, end=',') self.in_order(root.rchild) tree = BST() for i in [1,5,9,8,7,6,4,3,2]: tree.insert_no_rec(i) tree.in_order(tree.root) #print(tree.query_no_rec(12))
二叉搜索樹的應用——AVL樹、B樹、B+樹
AVL樹
AVL樹:AVL樹是一棵自平衡的二叉搜索樹。
AVL樹具有以下性質: 根的左右子樹的高度之差的絕對值不能超過1 根的左右子樹都是平衡二叉樹
AVL的實現方式:旋轉
B樹
B樹是一棵自平衡的多路搜索樹。常用於數據庫的索引。
在B-樹中查找給定關鍵字的方法是,首先把根結點取來,在根結點所包含的關鍵字K1,…,Kn查找給定的關鍵字(可用順序查找或二分查找法),若找到等於給定值的關鍵字,則查找成功;否則,一定可以確定要查找的關鍵字在Ki與Ki+1之間,Pi為指向子樹根節點的指針,此時取指針Pi所指的結點繼續查找,直至找到,或指針Pi為空時查找失敗。
B+ 樹
B+樹的查找
1
2
3
4
5
6
7
8
9
10
|
Function: search (k)
return
tree_search (k, root); Function: tree_search (k, node)
if
node is a leaf then
return
node;
switch
k
do
case
k < k_0
return
tree_search(k, p_0);
case
k_i ≤ k < k_{i+
1
}
return
tree_search(k, p_{i+
1
});
case
k_d ≤ k
return
tree_search(k, p_{d+
1
});
//偽代碼假設沒有重復值
|
B+樹的插入
B+樹的刪除