樹是一種很是重要的非線性數據結構,直觀的看,它是數據元素(在樹中稱爲節點)按分支關係組織起來的結構,很像天然界中樹那樣。樹結構在客觀世界中普遍存在,如人類社會的族譜和各類社會組織機構均可用樹形象表示。樹在計算機領域中也獲得了普遍應用,如在編譯源程序時,可用樹表示源程序的語法結構。又如在數據庫系統中,樹型結構也是信息的重要組織形式之一。一切具備層次關係的問題均可以用樹來描述。html
樹(Tree)是元素的集合。樹的定義是遞歸的,樹是一種遞歸的數據結構。好比:目錄結構。樹是由n個結點組成的集合:若是n=0,那這就是一顆空樹;若是 n>0,那麼存在1個結點做爲樹的根節點,其餘結點能夠分爲m個集合,每一個集合自己又是一棵樹。node
以下圖,咱們分別解釋:git
1)B是K的祖先結點,K是B的子孫節點,E是K的雙親節點,K是E的孩子節點,K是L的兄弟節點。github
2)樹中一個結點的子節點個數爲該節點的度,樹中結點最大度數爲樹的度。算法
3)度大於0爲節點結點,度等於0爲葉子結點。數據庫
4)結點層次如圖,結點深度時從根結點從頂往下累加,結點高度從低往上累加,樹的高度(深度)是樹的最大層數。數組
5)有序樹:從左到右有次序,有關聯。反之爲無序樹。數據結構
6)兩結點之間的路徑是兩個結點之間所通過的結點序列構成的,路徑長度是路徑上所通過的邊的個數。app
7)森林是 m (m >=0)棵互不相交的集合。dom
上面觀察實際上給了咱們一種嚴格的定義樹的方法:
樹的示意圖已經給出了樹的一種內存實現方法:每一個節點存儲元素和多個指向子節點的指針。然而,子節點數目的是不肯定的。一個父節點可能有大量的子節點,而另外一個父節點可能只有一個子節點,而樹的增刪節點操做會讓子節點的數目發生進一步的變換。這種不肯定性就可能就可能帶來大量的內存相關操做,而且容易形成內存的浪費。
一種經典的實現方法以下:
樹的內存實現:擁有同一父節點的兩個結點互爲兄弟節點(sibling)。上圖的實現方式中,每一個節點包含一個指針指向第一個子節點,而且有另外一個指針指向他的下一個兄弟節點。這樣,咱們就能夠用統一的,肯定的結構來表示每一個節點。
代碼以下:
#_*_coding:utf-8_*_ class Node: def __init__(self, name, type='dir'): self.name = name self.type = type # 'dir' or ; 'file' self.children = [] self.parent = None # 鏈式存儲 def __repr__(self): return self.name class FileSystemTree: def __init__(self): self.root = Node("/") # 首先咱們建立一個根目錄 self.now = self.root def mkdir(self, name): # 建立一個文件目錄,因此咱們必須保證name是以 /結尾,若是沒有,咱們就加 if name[-1] != '/': name += '/' node = Node(name) # 建立一個文件目錄 self.now.children.append(node) node.parent = self.now def ls(self): # 展現當前文件夾下的文件 return self.now.children def cd(self, name): # 切換到指定目錄 注意:支持絕對路徑和相對路徑 # 相對路徑是從now的路徑下開始,而絕對路徑是從root路徑下開始找 if name[-1] != '/': name += '/' if name == '../': self.now = self.now.parent return for child in self.now.children: if child.name == name: # 若是傳入的目錄名等於孩子的目錄名,咱們直接切換 self.now = child return raise ValueError("invalid dir") tree = FileSystemTree() tree.mkdir('var/') tree.mkdir('bin/') tree.mkdir('usr/') print(tree.ls()) # [var/, bin/, usr/] tree.cd('bin/') print(tree.ls()) # [] print(tree.root.children) # [var/, bin/, usr/]
二叉樹的鏈式存儲:將二叉樹的節點定義爲一個對象,節點之間經過相似鏈表的連接方式來鏈接。
二叉樹是一種特殊的樹,它具備如下特色:
1)至多隻有兩棵子樹,二叉樹有左右之分,次序不能顛倒,也是遞歸形式定義。
2)或者爲空二叉樹,即 n=0
3)或者由一個根結點和兩個互不相交的被稱爲根的左子樹和右子樹組成。左子樹和右子樹又分別是一顆二叉樹。
4)每一個節點至多有兩個節點,即每一個節點的度最多爲2
5)二叉樹中全部節點的形態有5種:空節點,無左右子樹的節點,只有左子樹的節點,只有右子樹的節點和具備左右子樹的節點
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 print(root.lchild.rchild.data)
二叉樹的節點定義:
class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None # 左孩子 self.rchild = None # 右孩子
1)度爲2的樹至少有3個結點,而二叉樹能夠爲空。
2)左右次數。
二叉樹的存儲結構分爲鏈式存儲結構和順序存儲結構(列表)
二叉樹的順序存儲方式
思考:父節點和左孩子節點的編號下標有什麼關係?
0-1 1-3 2-5 3-7 4-9 i ----> 2i+1
父節點和右孩子節點的編號下標有有什麼關係?
0-2 1-4 2-6 3-8 4-10 i -----> 2i+2
二叉樹的鏈式存儲
結構採用鏈式存儲二叉樹中的數據元素,用鏈創建二叉樹中結點之間的關係。二叉樹最經常使用的鏈式存儲結構是二叉鏈,每一個節點包含三個域,分別是數據元素域 data,左孩子鏈域 LChild 和 右孩子鏈域 rChild。與單鏈錶帶頭結點和不帶頭節點的兩種狀況類似,二叉鏈存儲結構的二叉樹也有帶頭結點和不帶頭節點兩種。
那麼如何遍歷一顆二叉樹呢?其實有兩種通用的遍歷樹策略:
在這個策略中,咱們採用深度做爲優先級,以便從根開始一直到達某個肯定的葉子,而後再返回根到達另外一個分支。
深度優先搜索策略又能夠根據根節點,左孩子和右孩子的相對順序被細分爲先序遍歷,中序遍歷和後序遍歷。
咱們按照高度順序一層一層的訪問整棵樹,高層次的節點將會被低層次的節點先被訪問到。
下圖中的頂點按照訪問的順序編號,按照1-2-3-4-5 的順序來比較不一樣的策略:
下面學習二叉樹的遍歷方式,如下圖的二叉樹爲例,咱們分別學習前序遍歷,中序遍歷,後序遍歷,層次遍歷。
思想:先訪問根節點,再先序遍歷左子樹,而後再序遍歷右子樹。總的來講是 根——左——右
前序遍歷如圖所示:
代碼以下:
# 二叉樹的前序遍歷 def pre_order(root): if root: print(root.data) # 先打印根節點 pre_order(root.lchild) pre_order(root.rchild) # pre_order(root) ''' E A C B D G F '''
思想:先中序訪問左子樹,再序訪問根節點,最後中序遍歷右子樹。總的來講是 左——根——右
中序遍歷如圖所示:
代碼如圖所示:
# 中序遍歷 def in_order(root): if root: in_order(root.lchild) print(root.data) in_order(root.rchild) # in_order(root) ''' A B C D E G F '''
思想:前後續訪問左子樹,而後後續訪問右子樹,最後訪問根,總的來講是 左——右——根
後序遍歷如圖所示:
代碼以下:
# 後序遍歷 def post_order(root): if root: post_order(root.lchild) post_order(root.rchild) print(root.data) post_order(root) ''' B D C A F G E '''
思想:利用隊列,依次將根,左子樹,右子樹存入隊列,按照隊列的先進先出規則來實現層次遍歷。
按照上面的例子:
簡單來講就是:根節點進隊,而後出隊,接着孩子節點入隊,當隊列爲空則中止循環。
當E進隊,而後E出隊,E出隊後,他的左孩子和右孩子進隊,也就是AG;而後A出隊,他沒有左孩子,右孩子C進隊,而後G出隊,它沒有左孩子,右孩子F進隊。。。。。。
代碼以下:
from collections import deque def level_order(root): queue = deque() queue.append(root) while len(queue) > 0: # 只要隊不空 node = queue.popleft() print(node.data) if node.lchild: queue.append(node.lchild) if node.rchild: queue.append(node.rchild) level_order(root) ''' E A G C F B D '''
滿二叉樹做爲一種特殊的二叉樹,它是指:除了葉子節點,全部節點都有兩個孩子(左子樹和右子樹),而且全部葉子節點深度都同樣。
其特色有:
徹底二叉樹是由滿二叉樹引伸而來,假設二叉樹深度爲 h,那麼除了第h層外,以前的每一層(1~h-1)的節點數都達到最大,即沒有空的位置,並且第K層的子節點也都集中在左子樹上(順序)。
其具備如下特色:
一顆二叉樹或者空二叉樹,如:左子樹上全部關鍵字均小於根結點的關鍵字,右子樹上的全部結點的關鍵字均大於根結點的關鍵字,左子樹和右子樹各是一顆二叉排序樹。
樹上任何一結點的左子樹和右子樹的深度只差不超過1 。
二叉搜索樹(Binary Search Tree),又名二叉排序樹(Binary Sort Tree)。
因爲二叉樹的子節點數目肯定,因此能夠直接採用下圖方式在內存中實現。每一個節點有一個左子節點(left children)和右子節點(right children)。左子節點是左子樹的根節點,右子節點是右子樹的根節點。
若是咱們給二叉樹加一個額外的條件,就能夠獲得一種被稱爲二叉搜索樹(binary search tree)的特殊二叉樹。二叉搜索樹要求:每一個節點都不比它左子樹的任意元素小,並且不比它的右子樹的任意元素大。
二叉搜索樹是一顆二叉樹且知足性質:設x是二叉樹的一個節點。若是 y 是 x 左子樹的一個節點,那麼 y.key <= x.key;若是y 是x 的右子樹的一個節點,那麼 y.key >= x.key。
二叉搜索樹,注意樹中元素的大小。二叉搜索樹能夠方便的實現搜索算法。在搜索元素 x 的時候,咱們能夠將 x 和根節點比較:
二叉搜索樹所須要進行的操做次數最多與樹的深度相等。n個結點的二叉搜索樹的深度最多爲 n ,最少爲 log(n).
從根節點開始,若插入的值比根節點的值小,則將其插入根節點的左子樹;若比根節點的值大,則將其插入根節點的右子樹。該操做可使用遞歸進行實現。
代碼以下:
class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None # 左孩子 self.rchild = None # 右孩子 self.parent = None class BST: def __init__(self, li=None): self.root = None if li: for val in li: self.insert_no_rec(val) def insert(self, node, val): if not node: node = BiTreeNode(val) elif val < node.data: node.lchild = self.insert(node.lchild, val) node.lchild.parent = node elif val > node.data: node.rchild = self.insert(node.rchild, val) node.rchild.parent = node return node 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) p.lchild.parent = p return elif val > p.data: if p.rchild: p = p.rchild else: p.rchild = BiTreeNode(val) p.rchild.parent = p return else: return
從根節點開始查找,待查找的值是否與根節點的值相同,若相同則返回True;不然,判斷待尋找的值是否比根節點的值小,如果則進入根節點左子樹進行查找,不然進入右子樹進行查找。該操做使用遞歸實現。
代碼以下:
def query(self, node, val): if not node: return None if node.data < val: return self.query(node.rchild, val) elif node.data > val: return self.query(node.lchild, val) else: return node
查找最小值:從根節點開始,沿着左子樹一直往下,直到找到最後一個左子樹節點,按照定義可知,該節點必定是該二叉搜索樹中的最小值節點。
程序代碼以下:
def findMin(self, root): '''查找二叉搜索樹中最小值點''' if root.left: return self.findMin(root.left) else: return root
查找最大值:從根節點開始,沿着右子樹一直往下,知道找到最後一個右子樹節點,按照定義可知,該節點必定是該二叉搜索樹中的最大值節點。
程序代碼以下:
def findMax(self, root): '''查找二叉搜索樹中最大值點''' if root.right: return self.findMax(root.right) else: return root
對二叉搜索樹節點的刪除操做分爲如下三種狀況
1,若是要刪除的節點是葉子節點:直接刪除
2,若是要刪除的節點只有一個孩子:將此節點的父親與孩子鏈接,而後刪除該節點(注意:該待刪節點可能只有左子樹或者右子樹)
3,若是要刪除的節點有兩個孩子:將其右子樹的最小節點(該節點最多有一個右孩子)刪除,並替換當前節點
代碼以下:
# _*_coding:utf-8_*_ import random class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None # 左孩子 self.rchild = None # 右孩子 self.parent = None class BST: def __init__(self, li=None): self.root = None if li: for val in li: self.insert_no_rec(val) def __remove_node_1(self, node): # 第一種狀況:node是葉子節點 if not node.parent: self.root = None if node == node.parent.lchild: # node是他父親的左孩子 node.parent.lchild = None node.parent = None # 能夠不寫 else: # node是他父親的右孩子 node.parent.rchild = None def __remove_node_21(self, node): # 狀況2.1:node只有一個孩子,且爲左孩子 if not node.parent: # 根節點 self.root = node.lchild node.lchild.parent = None elif node == node.parent.lchild: # node是它父親的左孩子 node.parent.lchild = node.lchild node.lchild.parent = node.parent else: # node 是它父親的右孩子 node.parent.rchild = node.lchild node.lchild.parent = node.parent def __remove_node_22(self, node): # 狀況2.2:node只有一個孩子,且爲右孩子 if not node.parent: self.root = node.rchild elif node == node.parent.lchild: node.parent.lchild = node.rchild node.rchild.parent = node.parent else: node.parent.rchild = node.rchild node.rchild.parent = node.parent def delete(self, val): if self.root: # 不是空樹 node = self.query_no_rec(val) if not node: # 不存在 return False if not node.lchild and not node.rchild: # 1,葉子節點 self.__remove_node_1(node) elif not node.rchild: # 2.1 只有一個左孩子 self.__remove_node_21(node) elif not node.lchild: # 2.2 只有一個右孩子 self.__remove_node_22(node) else: # 3,兩個孩紙都有 min_node = node.rchild while min_node.lchild: # 有左孩子 min_node = min_node.lchild node.data = min_node.data # 刪除min_node if min_node.rchild: self.__remove_node_22(min_node) else: self.__remove_node_1(min_node)
實現二叉搜索樹的前序遍歷,中序遍歷,後序遍歷,並打印出來。其中中序遍歷打印出來的數列是按照遞增順序排列。
程序的代碼以下:
def printTree(self, root): # 打印二叉搜索樹(中序打印,有序數列—) if root == None: return self.printTree(root.left) print(root.val, end=',') self.printTree(root.right)
代碼以下:
# _*_coding:utf-8_*_ import random class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None # 左孩子 self.rchild = None # 右孩子 self.parent = None class BST: def __init__(self, li=None): self.root = None if li: for val in li: self.insert_no_rec(val) def insert(self, node, val): if not node: node = BiTreeNode(val) elif val < node.data: node.lchild = self.insert(node.lchild, val) node.lchild.parent = node elif val > node.data: node.rchild = self.insert(node.rchild, val) node.rchild.parent = node return node 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) p.lchild.parent = p return elif val > p.data: if p.rchild: p = p.rchild else: p.rchild = BiTreeNode(val) p.rchild.parent = p return else: return def query(self, node, val): if not node: return None if node.data < val: return self.query(node.rchild, val) elif node.data > val: return self.query(node.lchild, val) else: return node def query_no_rec(self, val): p = self.root while p: if p.data < val: p = p.rchild elif p.data > val: p = p.lchild else: return p return None def pre_order(self, root): if root: print(root.data, end=',') self.pre_order(root.lchild) self.pre_order(root.rchild) def in_order(self, root): if root: self.in_order(root.lchild) print(root.data, end=',') self.in_order(root.rchild) def post_order(self, root): if root: self.post_order(root.lchild) self.post_order(root.rchild) print(root.data, end=',') def __remove_node_1(self, node): # 第一種狀況:node是葉子節點 if not node.parent: self.root = None if node == node.parent.lchild: # node是他父親的左孩子 node.parent.lchild = None node.parent = None # 能夠不寫 else: # node是他父親的右孩子 node.parent.rchild = None def __remove_node_21(self, node): # 狀況2.1:node只有一個孩子,且爲左孩子 if not node.parent: # 根節點 self.root = node.lchild node.lchild.parent = None elif node == node.parent.lchild: # node是它父親的左孩子 node.parent.lchild = node.lchild node.lchild.parent = node.parent else: # node 是它父親的右孩子 node.parent.rchild = node.lchild node.lchild.parent = node.parent def __remove_node_22(self, node): # 狀況2.2:node只有一個孩子,且爲右孩子 if not node.parent: self.root = node.rchild elif node == node.parent.lchild: node.parent.lchild = node.rchild node.rchild.parent = node.parent else: node.parent.rchild = node.rchild node.rchild.parent = node.parent def delete(self, val): if self.root: # 不是空樹 node = self.query_no_rec(val) if not node: # 不存在 return False if not node.lchild and not node.rchild: # 1,葉子節點 self.__remove_node_1(node) elif not node.rchild: # 2.1 只有一個左孩子 self.__remove_node_21(node) elif not node.lchild: # 2.2 只有一個右孩子 self.__remove_node_22(node) else: # 3,兩個孩紙都有 min_node = node.rchild while min_node.lchild: # 有左孩子 min_node = min_node.lchild node.data = min_node.data # 刪除min_node if min_node.rchild: self.__remove_node_22(min_node) else: self.__remove_node_1(min_node) def printTree(self, root): # 打印二叉搜索樹(中序打印,有序數列—) if root == None: return self.printTree(root.left) print(root.val, end=',') self.printTree(root.right) # 刪除 tree = BST([1, 4, 2, 5, 3, 8, 6, 9, 7]) tree.in_order(tree.root) print(" ") tree.delete(4) tree.delete(1) tree.delete(8) tree.in_order(tree.root) ''' # 插入操做 tree = BST([4,6,7,9,2,1,3,5,8]) tree.pre_order(tree.root) print(" ") tree.in_order(tree.root) print(" ") tree.post_order(tree.root) print(" ") ''' 4, 2, 1, 3, 6, 5, 7, 9, 8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 3, 2, 5, 8, 9, 7, 6, 4, ''' # 查詢操做 li = list(range(0, 500, 2)) random.shuffle(li) tree = BST(li) print(tree.query_no_rec(4).data) # 4 '''
平均狀況下,二叉搜索樹進行搜索的時間複雜度爲O(nlgn)
最壞狀況下,二叉搜索樹可能很是偏斜,以下圖所示:
解決方案:
在計算機科學中,AVL樹(發明此樹的三位科學家的名字首字母)是最先被髮明的自平衡二叉查找樹。在AVL樹中,任一節點對應的兩棵子樹的最大高度爲1,所以他也被稱爲高度平衡樹。查找,插入和刪除在平均和最壞的狀況下的時間複雜度都是 O(log n)。增長和刪除元素的操做則可能須要藉由一次或屢次樹旋轉,以實現樹的從新平衡。
節點的平衡因子是它的左子樹的高度減去它的右子樹的高度(有時相反)。帶有平衡因子1, 0或者-1的節點被認爲是平衡的。帶有平衡因子 -2 或 2的節點被認爲是不平衡的,並須要從新平衡這個樹,平衡因子能夠直接存儲在每一個節點中,或從可能存儲在節點的子樹高度計算出來。
AVL樹是一顆自平衡的二叉搜索樹。通常要求每一個節點的左子樹和右子樹的高度最多差1(空樹的高度定義爲 -1)。在高度爲 h 的AVL樹中,最少的節點數 S(h) = S(h-1) + S(h-2) + 1 獲得,其中 S(0) = 1, S(1) = 2。
以下圖所示,分別爲高度爲0, 1, 2, 3的AVL樹所須要的最少節點數:
旋轉是AVL樹最重要的操做了,理解了旋轉就理解了AVL樹的實現原理。
左單旋轉
下圖節點上面的數字表示平衡因子
如上圖所示,插入13後,右邊子樹11節點的平衡因子變爲了2(左右節點的高度差),整個AVL樹開始不平衡,這時便要開始以12爲軸心進行一次左單旋轉。具體旋轉操做時原來11的父節點10指向12,12的左節點指向11,而11的右節點指向原來的12的左節點(此例中,12的左節點爲空)。
右單旋轉
上圖中插入3後左子樹不平衡了,根節點8的平衡因子變爲了-2,此時應該以6爲軸心向右單旋轉一次,具體操做與左單旋轉相似:8的左節點指向6的有節點(此時爲7),6的右節點指向8,因爲8原來是跟節點,沒有父節點,因此根節點指向6.旋轉後6和8節點都恢復0的平衡因子了。
左右雙旋轉
如上圖所示,10節點的平衡因子是 -2,而它的左節點的平衡因子卻爲1,兩個節點失去平衡的方向不同,因此要先以7位軸心對節點6左單旋轉一次,再以7爲軸心對節點10右旋轉一次。操做細節與上面單次循環同樣。注意此時操做的3個結點的平衡因子要根據不一樣7的平衡因子單獨調整。
右左雙旋轉
如上圖所示,當一個節點的平衡因子爲2,而它的右子節點的平衡因子爲-1時,就須要先進行右旋轉,再左旋轉。注意中間節點13右旋轉後的平衡因子變爲1了。代碼同左右雙旋轉相似。
插入一個節點可能會破壞 AVL樹的平衡,能夠經過旋轉操做來進行修正。
插入一個節點後,只有從插入節點到根節點的路徑上的節點的平衡可能被改變。咱們須要找出第一個破壞了平衡條件的節點,稱之爲K,K的兩棵子樹的高度差2.
不平衡的出現可能有四種狀況:
1,對K的左兒子的左子樹進行一次插入
2,對K的左兒子的左子樹進行一次插入
3,對K的右兒子的左子樹進行一次插入
4,對K的右兒子的右子樹進行一次插入
狀況1和4是對稱的,須要進行一次單旋轉操做,狀況2與3須要一次雙旋轉操做。
不平衡是因爲對K的右孩子的右子樹插入致使的:左旋
那代碼過程以下圖所示:
代碼以下:
#_*_coding:utf-8_*_ from bst import BiTreeNode, BST class AVLNode(BiTreeNode): def __init__(self, data): BiTreeNode.__init__(self, data) self.bf = 0 class AVLTree(BST): def __init__(self, li=None): BST.__init__(self, li) def rotate_left(self, p, c): s2 = c.lchild p.rchild = s2 if s2: s2.parent = p c.lchild = p p.parent = c p.bf = 0 c.bf = 0
不平衡是因爲對K的左孩子的左子樹插入致使的:右旋
右旋插入的過程以下圖:
代碼以下:
#_*_coding:utf-8_*_ from bst import BiTreeNode, BST class AVLNode(BiTreeNode): def __init__(self, data): BiTreeNode.__init__(self, data) self.bf = 0 class AVLTree(BST): def __init__(self, li=None): BST.__init__(self, li) def rotate_right(self, p, c): s2 = c.rchild p.lchild = s2 if s2: s2.parent = p c.rchild = p p.parent = c p.bf = 0 c.bf = 0
不平衡是因爲對K的右孩子的左子樹插入致使的:右旋-左旋
右旋左旋的代碼流程如圖所示:
代碼以下:
#_*_coding:utf-8_*_ from bst import BiTreeNode, BST class AVLNode(BiTreeNode): def __init__(self, data): BiTreeNode.__init__(self, data) self.bf = 0 class AVLTree(BST): def __init__(self, li=None): BST.__init__(self, li) def rotate_right_left(self, p, c): g = c.lchild s3 = g.rchild c.lchild = s3 if s3: s3.parent = c g.rchild = c c.parent = g s2 = g.lchild p.rchild = s2 if s2: s2.parent = p g.lchild = p p.parent = g # 更新bf if g.bf > 0: p.bf = -1 c.bf = 0 elif g.bf < 0: p.bf = 0 c.bf = 1 else : # 插入的是g p.bf = 0 c.bf = 0
還有一種不平衡是因爲對K的左孩子的右子樹插入致使的:左旋-右旋
代碼的流程以下:
代碼以下:
#_*_coding:utf-8_*_ from bst import BiTreeNode, BST class AVLNode(BiTreeNode): def __init__(self, data): BiTreeNode.__init__(self, data) self.bf = 0 class AVLTree(BST): def __init__(self, li=None): BST.__init__(self, li) def rotate_left_right(self, p, c): g = c.rchild s2 = g.lchild c.rchild = s2 if s2: s2.parent = c g.lchild = c c.parent = g s3 = g.rchild p.lchild = s3 if s3: s3.parent = p g.rchild = p p.parent = g # 更新bf if g.bf < 0: p.bf = 1 c.bf = 0 elif g.bf > 0: p.bf = 0 c.bf = -1 else: p.bf = 0 c.bf = 0
刪除操做比較複雜。
1,當前節點爲要刪除的節點且是樹葉(無子樹),直接刪除,當前節點(爲None)的平衡不受影響。
2.當前節點爲要刪除的節點且只有一個左兒子或右兒子,用左兒子或右兒子代替當前節點,當前節點的平衡不受影響。
3.當前節點爲要刪除的節點且有左子樹右子樹:若是右子樹高度較高,則從右子樹選取最小節點,將其值賦予當前節點,而後刪除右子樹的最小節點。若是左子樹高度較高,則從左子樹選取最大節點,將其值賦予當前節點,而後刪除左子樹的最大節點。這樣操做當前節點的平衡不會被破壞。
4.當前節點不是要刪除的節點,則對其左子樹或者右子樹進行遞歸操做。當前節點的平衡條件可能會被破壞,須要進行平衡操做。
如上圖,25爲當前節點,左子樹刪除17後平衡條件被破壞,須要根據當前節點(25)的右子樹(30)的左子樹(28)高度是否高於右子樹(35)的高度進行判斷,若高於,進行雙旋轉,不然進行單旋轉。
B樹(B-Tree):B 樹是一顆自平衡的多路搜索樹,經常使用語數據庫的索引。
學習過程當中不免遇到理解的問題:圖形化能很好的幫助咱們理解問題,下面是兩個在線生成二叉樹的網址,根據本身須要看看,添加
利用PyGraphviz模塊畫出二叉樹
參考網址:http://pygraphviz.github.io/documentation/pygraphviz-1.5/ 這裏有詳細的使用說明
安 裝該模塊失敗,參考這篇博客 https://blog.csdn.net/chirebingxue/article/details/50393755
使用了該模塊以完成最後生成的二叉樹顯示,代碼以下
import pygraphviz as pgv def draw(self, filename='./tree.png'): g = pgv.AGraph(strict=False, directed=True) g.node_attr['shape'] = 'circle' def traver(node): if node: if not node.parent: g.add_node(node.key) else: g.add_edge(node.parent.key, node.key) traver(node.left) traver(node.right) traver(self.root) g.layout('dot') g.draw(filename)
簡單的測試改模塊的效果
tree = AVLTree() tree.insert(range(0, 20, 2)) # 本身簡單實現了個能夠接受一個可迭代對象的數值的插入 tree.draw() tree.delete_key(14) tree.draw('tree2.png')
最後生成下面的PNG圖
參考文獻:樹:https://www.cnblogs.com/hwnzy/p/11118942.html
二叉搜索樹:https://www.cnblogs.com/lliuye/p/9118591.html
AVL樹:https://www.cnblogs.com/yscl/p/10077607.html