树是一种很是重要的非线性数据结构,直观的看,它是数据元素(在树中称为节点)按分支关系组织起来的结构,很像天然界中树那样。树结构在客观世界中普遍存在,如人类社会的族谱和各类社会组织机构均可用树形象表示。树在计算机领域中也获得了普遍应用,如在编译源程序时,可用树表示源程序的语法结构。又如在数据库系统中,树型结构也是信息的重要组织形式之一。一切具备层次关系的问题均可以用树来描述。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