700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 五.树 二叉树 二叉搜索树(BST)和自平衡二叉搜索树(AVL)

五.树 二叉树 二叉搜索树(BST)和自平衡二叉搜索树(AVL)

时间:2020-09-25 09:09:30

相关推荐

五.树 二叉树 二叉搜索树(BST)和自平衡二叉搜索树(AVL)

1.树

树是一种数据结构 比如:目录结构

树是一种可以递归定义的数据结构

树是由n个节点组成的集合:

如果 n=0, 那这是一颗空树

如果 n>0, 那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一个树

一些概念:

根节点,叶子节点

树的深度(高度)

树的度:这个树中所有节点最大的度(孩子节点个数)

孩子节点/父节点

子树

2.用树简单模拟文件系统

"""模拟文件系统"""class Node:def __init__(self, name, type='dir'):self.name = nameself.type = typeself.children = []self.parent = Nonedef __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.nowdef ls(self):print(self.now.children)def cd(self, name):if name[-1] != "/":name += "/"if name == "../":self.now = self.now.parentreturnfor child in self.now.children:if child.name == name:self.now = childbreakelse:raise ValueError("invalid dir.")tree = FileSystemTree()tree.mkdir("var/")tree.mkdir("bin")tree.mkdir("usr")print(tree.root.children)tree.ls()tree.cd("bin")tree.mkdir("python")tree.ls()tree.cd("../")tree.ls()

3.二叉树

二叉树:度不超过二的树

二叉树的遍历方式:

前序遍历

中序遍历

后序遍历

层序遍历

#!/usr/bin/env python# -*- coding:utf-8 -*-# author: Xiang Hai# wechat: xiaoyou42952"""二叉树:度不超过二的树"""class BiTreeNode:def __init__(self, data):self.data = dataself.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 = ae.rchild = ga.rchild = cc.lchild = bc.rchild = dg.rchild = froot = eprint(root.lchild.rchild.data)"""二叉树的遍历方式:前序遍历中序遍历后序遍历层序遍历"""def pre_order(root):if root:print(root.data, end=',')pre_order(root.lchild)pre_order(root.rchild)pre_order(root)def in_order(root):if root:in_order(root.lchild)print(root.data, end=',')in_order(root.rchild)print()in_order(root)def post_order(root):if root:post_order(root.lchild)post_order(root.rchild)print(root.data, end=',')print()post_order(root)#利用队列实现from collections import dequedef level_order(root):q = deque()if root:q.append(root)while len(q) > 0:node = q.popleft()print(node.data, end = ",")if node.lchild:q.append(node.lchild)if node.rchild:q.append(node.rchild)print()level_order(root)"""知道二叉树的前序遍历(或者后序遍历)和中序遍历,可以还原出这个二叉树"""

知道二叉树的前序遍历(或者后序遍历)和中序遍历,可以还原出这个二叉树

4.二叉搜索树BST

二叉搜索树是一颗二叉树且满足性质:

任意节点上值为key

它的左子树上节点值都比key小

右子树上节点值都比key大

基本操作:

查询 O(logn)

插入 O(logn)

删除:

1.如果要删除的是叶子节点,直接删除

2.如果要删除的节点只有一个孩子,将此节点的父亲与孩子连接,然后删除该节点

如果要删除的节点是根,需要重新更新根节点

3.如果要删除的节点有两个孩子:将其右子树的最小节点删除,并替换当前节点

#!/usr/bin/env python# -*- coding:utf-8 -*-# author: Xiang Hai# wechat: xiaoyou42952"""二叉搜索树是一颗二叉树且满足性质:任意节点上值为key它的左子树上节点值都比key小右子树上节点值都比key大基本操作:查询 O(logn)插入 O(logn)删除:1.如果要删除的是叶子节点,直接删除2.如果要删除的节点只有一个孩子,将此节点的父亲与孩子连接,然后删除该节点如果要删除的节点是根,需要重新更新根节点3.如果要删除的节点有两个孩子:将其右子树的最小节点删除,并替换当前节点"""class BiTreeNode:def __init__(self, data):self.data = dataself.lchild = None # 左孩子self.rchild = None # 右孩子self.parent = Noneclass BST:def __init__(self, li=None):self.root = Noneif li:for val in li:self.insert_no_recur(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 = nodeelif val > node.data:node.rchild = self.insert(node.rchild, val)node.rchild.parent = nodereturn nodedef insert_no_recur(self, val):p = self.rootif not p: # 空树self.root = BiTreeNode(val)returnwhile True:if val < p.data:if not p.lchild:p.lchild = BiTreeNode(val)p.lchild.parent = preturnelse:p = p.lchildelif val > p.data:if not p.rchild:p.rchild = BiTreeNode(val)p.rchild.parent = preturnelse:p = p.rchildelse:returndef 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 query(self, node, val):if not node: # 递归终止条件return Noneif node.data < val:return self.query(node.rchild, val)elif node.data > val:return self.query(node.lchild, val)else:return nodedef query_no_recur(self, val):p = self.rootwhile p:if p.data < val:p = p.rchildelif p.data > val:p = p.lchildelse:return pelse:return Nonedef __remove_node_1(self, node):# 1.node是叶子节点if not node.parent: # node是根节点self.root = Noneelif node == node.parent.lchild:node.parent.lchild = Noneelse:node.parent.rchild = Nonedef __remove_node_21(self, node):# 2.node只有一个左孩子if not node.parent: # 根节点self.root = node.lchildnode.lchild.parent = Noneelif node == node.parent.lchild:node.parent.lchild = node.lchildnode.lchild.parent = node.parentelse:node.parent.rchild = node.lchildnode.lchild.parent = node.parentdef __remove_node_22(self, node):# 3.node只有一个右孩子if not node.parent:self.root = node.rchildelif node == node.parent.lchild:node.parent.lchild = node.rchildnode.rchild.parent = node.parentelse:node.parent.rchild = node.rchildnode.rchild.parent = node.parentdef delete(self, val):if self.root:node = self.query_no_recur(val)if not node: # 不存在return Falseif not node.lchild and not node.rchild: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: # node有两个孩子min_node = node.rchildwhile min_node.lchild:min_node = min_node.lchildnode.data = min_node.data# 删除min_nodeif min_node.rchild:self.__remove_node_22(min_node)else:self.__remove_node_1(min_node)tree = BST([4,6,7,9,2,1,3,5,8])tree.pre_order(tree.root)print()tree.in_order(tree.root)print()print(tree.query_no_recur(10))tree.delete(4)tree.in_order(tree.root)"""平均情况下,二叉搜索树查找的时间复杂度为O(logn)最坏情况下,二叉树偏斜退化成一条链表,查找时间复杂度O(n)解决方法:随机化插入AVL树"""

平均情况下,二叉搜索树查找的时间复杂度为O(logn)

最坏情况下,二叉树偏斜退化成一条链表,查找时间复杂度O(n)

解决方法:

随机化插入

AVL树

5.自平衡二叉搜索树AVL

AVL树:是一棵自平衡的二叉搜索树

任意节点的左右子树高度差最大为1

如何保持平衡:旋转

插入一个节点后,只有从插入节点到根节点路径上的节点的平衡可能被破坏,

我们需要找出第一个破坏了平衡的节点,称之为K,K的两棵子树高度差为2

不平衡可能有4种情况:(右右左,左左右,左右左右,右左右左)

1.对K的右孩子的右子树插入导致的:左旋

2.对K的左孩子的左子树插入导致的:右旋

3.对K的右孩子的左子树插入导致的:右旋-左旋

4.对K的左孩子的右子树插入导致的:左旋-右旋

#!/usr/bin/env python# -*- coding:utf-8 -*-# author: Xiang Hai# wechat: xiaoyou42952"""AVL树:是一棵自平衡的二叉搜索树任意节点的左右子树高度差最大为1如何保持平衡:旋转插入一个节点后,只有从插入节点到根节点路径上的节点的平衡可能被破坏,我们需要找出第一个破坏了平衡的节点,称之为K,K的两棵子树高度差为2不平衡可能有4种情况:(右右左,左左右,左右左右,右左右左)1.对K的右孩子的右子树插入导致的:左旋2.对K的左孩子的左子树插入导致的:右旋3.对K的右孩子的左子树插入导致的:右旋-左旋4.对K的左孩子的右子树插入导致的:左旋-右旋"""from _027_二叉搜索树 import BiTreeNode, BSTclass AVLNode(BiTreeNode):def __init__(self, data):BiTreeNode.__init__(self, data)self.bf = 0 # balance factor = 右子树高度-左子树高度class AVLTree(BST):def __init__(self, li = None):BST.__init__(self, li)def rotate_left(self, p, c):s2 = c.lchildp.rchild = s2if s2:s2.parent = pc.lchild = pp.parent = cp.bf = 0c.bf = 0return cdef rotate_right(self, p, c):s2 = c.rchildp.lchild = s2if s2:s2.parent = pc.rchild = pp.parent = cp.bf = 0c.bf = 0return cdef rotate_right_left(self, p, c):# 先右旋g = c.lchilds3 = g.rchildc.lchild = s3if s3:s3.parent = cg.rchild = cc.parent = g# 再左旋s2 = g.lchildp.rchild = 2if s2:s2.parent = pg.lchild = pp.parent = g# 更新bfif g.bf > 0:p.bf = -1c.bf = 0elif g.bf < 0:p.bf = 0c.bf = 1else: # 插入的是gp.bf = 0c.bf = 0g.bf = 0return gdef rotate_left_right(self, p, c):g = c.rchilds2 = g.lchildc.rchild = s2if s2:s2.parent = cg.lchild = cc.parent = gs3 = g.rchildp.lchild = s3if s3:s3.parent = pg.rchild = pp.parent = g# 更新bfif g.bf < 0:p.bf = 1c.bf = 0elif g.bf > 0:p.bf = 0c.bf = -1else: # 插入的是gp.bf = 0c.bf = 0g.bf = 0return gdef insert_no_recur(self, val):# 1. 和BST一样,先插入p = self.rootif not p: # 空树self.root = AVLNode(val)returnwhile True:if val < p.data:if not p.lchild:p.lchild = AVLNode(val)p.lchild.parent = pnode = p.lchildbreakelse:p = p.lchildelif val > p.data:if not p.rchild:p.rchild = AVLNode(val)p.rchild.parent = pnode = p.rchildbreakelse:p = p.rchildelse: # val == p.data重复return# 2. 更新balance factorwhile node.parent: # node的parent不空if node == node.parent.lchild: # 传递是从左子树来的# 更新node的parent的bf -= 1if node.parent.bf < 0: # 原来=-1,更新后变成-2,需要旋转了# 看node哪边沉,判断需要做哪种旋转g = node.parent.parent # 为了连接旋转后的子树x = node.parent # 旋转前子树的根if node.bf > 0:n = self.rotate_left_right(node.parent, node)else:n = self.rotate_right(node.parent, node)elif node.parent.bf > 0: # 1, 更新后变成0,传递终止node.parent.bf = 0breakelse: # 0, 更新后变-1node.parent.bf = -1node = node.parentcontinueelse: # 传递是从右子树来的# 更新后node的parent的bf += 1if node.parent.bf > 0: # 1 -> 2, 需要旋转g = node.parent.parentx = node.parent # 旋转前子树的根# 看node哪边沉if node.bf < 0:n = self.rotate_right_left(node.parent, node)else:n = self.rotate_left(node.parent, node)elif node.parent.bf < 0: # -1 -> 0, 传递终止node.parent.bf = 0breakelse: # 0->1node.parent.bf = 1node = node.parentcontinue# 连接旋转后的子树n.parent = gif g:if x == g.lchild:g.lchild = nelse:g.rchild = nbreakelse:self.root = nbreaktree = AVLTree([9,8,7,6,5,4,3,2,1])print()tree.pre_order(tree.root)print()tree.in_order(tree.root)"""AVL树的应用--B树B树是一个自平衡的多路搜索树,常用于数据库的索引,减少访问硬盘的次数一个节点存放n个值,有n+1个子节点"""

AVL树的应用–B树

B树是一个自平衡的多路搜索树,常用于数据库的索引,减少访问硬盘的次数

一个节点存放n个值,有n+1个子节点

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。