树
1.树的定义
树是一种数据结构,比如目录结构
树是一种可以递归定义的数据结构
树由n各节点组成的集合:(1)如果n=0,那么这是一棵空树;(2)如果n>0,那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树.
2.一些树的术语
(1)节点
(2)根节点:没有入边的节点
(3)父节点
(4)子节点
(5)兄弟节点
(6)子树
(7)叶子节点:没有子节点的节点
(8)树的高度(深度)
(9)树的度
实现文件系统:
class Node:
def __init__(self,name,type='dir'):
self.name = name
self.type = type
self.children = []
self.parent = None
def __str__(self):
return self.name
class FileSystemTree():
def __init__(self):
self.root = Node('/')
self.now = self.root
def mkdir(self, 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):
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('invaild dir')
二叉树
二叉树
二叉树是一个比较特殊的树,其每个节点的子节点不超过2;
实现二叉树
二叉树的链式存储:将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来连接.
节点定义:
class BtreeNode():
def __init__(self, data):
self.data = data
self.lchild = None
self.rchild = None
创建二叉树
a = BtreeNode('A')
b = BtreeNode('B')
c = BtreeNode('C')
d = BtreeNode('D')
e = BtreeNode('E')
f = BtreeNode('F')
g = BtreeNode('G')
e.lchild = a
e.rchild = g
a.rchild = c
c.lchild = b
c.rchild = d
g.rchild = f
二叉树的遍历
(1)前序遍历:根在前EACBDGF
(2)中序遍历:根在中间ABCDEGF
(3)后续遍历:根在后BDCAFGE
(4)层次遍历:从根开始逐层从左到右遍历EAGCFBD
代码实现:
root = e
# 前序遍历
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)
in_order(root)
def post_order(root):
if root:
post_order(root.lchild)
post_order(root.rchild)
print(root.data, end='')
from collections import deque
def level_order(root):
queue = deque()
queue.append(root)
while len(queue):
node = queue.popleft()
print(node.data,end='')
if node.lchild:
queue.append(node.lchild)
if node.rchild:
queue.append(node.rchild)
二叉堆实现优先级
二叉搜索树
二叉搜索树是一棵二叉树,且满足性质: 设x是二叉树的一个节点.如果y是x的左子树的一个节点,那么y.key<=x.key; 如果y是x的右子树的一个节点,那么y.key>=x.key.
即对于每个节点都满足左孩子小于父节点,右孩子大于父节点.
二叉搜索树的插入
class BtreeNode():
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_2(val)
def insert(self, node, val):
if not node:
node = BtreeNode(val)
elif val<node.data:
node.lchid = 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_2(self, val):
p = self.root
if not p:
self.root = BtreeNode(val)
return
while True:
if val < p.data:
if p.lchild:
p = p.lchild
else:
p.lchild = BtreeNode(val)
p.lchild.parent = p
return
elif val > p.data:
if p.rchild:
p = p.rchild
else:
p.rchild = BtreeNode(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.lchild, val)
elif node.data>val:
return self.query(node.rchild, val)
else:
return node
def query_2(self, val):
p = self.root
while p:
if p.data>val:
p = p.lchild
elif p.data < val:
p = p.rchild
else:
return p
return None
二插搜索树的删除
删除某个节点,必须要先找到该节点,再进行删除;则分为以下三种情况
(1)删除叶子节点:直接删除
(2)删除只有一个孩子的节点
当只有一个孩子时,我们把孩子和该节点的父母相连
(3)删除有两个孩子的节点
当有两个孩子时,我们把这个节点删掉,必须找到一个节点来填补这个节点的位置,我们取这个节点右子树的最小节点,(右子树的左子树一直找到最后一个左叶子节点,这时这个节点最多有一个右孩子)
如图,当要删除节点C时,找到其右子树的最小节点,从其右子树的左孩子一直找到左边最后一个左孩子就为最小的节点,并且这个孩子最多只有一个右孩子,或者是一个叶子节点.
def _del_1(self,node):
# 删除的是叶子节点
if not node.parent:
self.root = None
if node == node.parent.lchild:
node.parent.lchild = None
else:
node.parent.rchild = None
def _del_2(self, node):
# 删除的node只有一个左孩子
if not node.parent:
# 根节点
self.root = node.lchild
node.lchild.parent = None
elif node == node.parent.lchild:
node.parent.lchild = node.lchild
node.lchild.parent = node.parent
elif node == node.parent.rchild:
node.parent.rchild = node.lchild
node.lchild.parent = node.parent
def _del_3(self, node):
# 删除的node只有一个右孩子的情况
if not node.parent:
# 根节点
self.root = node.rchild
node.rchild.parent = None
elif node == node.parent.lchild:
node.parent.lchild = node.rchild
node.rchild.parent = node.parent
elif node == node.parent.rchild:
node.parent.rchild = node.rchild
node.rchild.parent = node.parent
def delete(self, val):
if self.root:
node = self.query_2(val)
if not node:
return False
elif not node.lchild and not node.rchild:
# 删除叶子节点
self._del_1(node)
elif not node.rchild:
self._del_2(node)
elif not node.lchild:
self._del_3(node)
else:
# 删除的节点有两个孩子,则将其右子树的最小节点(该节点最多有一个右孩子),替换到此位置.
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._del_3(min_node)
else:
self._del_1(min_node)
搜索二叉树的效率
搜索二叉树平均情况下进行搜索的时间复杂度为O(lgn)
最坏抢矿下二叉树十分偏斜:
解决方法:
(1)随机插入
(2)AVL平衡二叉树.
AVL平衡二叉树
AVL树是一棵自平衡二叉树搜索树.
AVL树具有以下性质:
(1)根的左右子树的高度之差的绝对值不能超过1;
(2)根的左右子树都是平衡二叉树
AVL树–插入与旋转
插入一个节点可能会破坏AVL树的平衡,可以通过旋转操作来进行修正.
插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变.我们需要找出第一个破坏了平衡条件的节点,称之为k.k的两颗子树的高度差2.
不平衡的出现可能有4种情况.
(1)不平衡是由于对k的左孩子的左子树插入导致的: 右旋
(2)不平衡是由于对k的右孩子的右子树插入导致的: 左旋
(3)不平衡是由于对k的右孩子的左子树插入导致的: 右旋-左旋
(4)不平衡是由于对k的左孩子的右子树插入导致的: 左旋-右旋
class AVLNode:
def __init__(self, data):
self.data = data
self.lchild = None
self.rchild = None
self.parent = None
self.bf = 0
from .BST import BST
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
return c
# 右旋
def rotate_right(self,p,c):
s2 = c.rchild
p.rchild = s2
if s2:
s2.parent = p
c.rchild = p
p.parent = c
p.bf = 0
c.bf = 0
return c
# 右旋-左旋
def right_left(self,p,c):
g = c.lchild
s2 = g.lchild
s3 = g.rchild
c.lchild = s3
if s3:
s3.parent = c
g.rchild = c
c.parent = g
p.rchild = s2
if s2:
s2.parent = p
g.lchild = p
p.parent = g
# 跟新bf
if g.bf>0: #g.bf = 1
p.bf = -1
c.bf = 0
elif g.bf < 0:
p.bf = 0
c.bf = 1
else:
p.bf = 0
c.bf = 0
g.bf = 0
return g
# 左旋-右旋
def rotate_left_right(self,p,c):
g = c.rchild
s2 = g.lchild
s3 = g.rchild
p.lchild = s3
if s3:
s3.parent = p
g.rchild = p
p.parent = g
c.rchild = s2
if s2:
s2.parent = c
g.lchild = c
c.parent = g
# 跟新bf
if g.bf > 0: # g.bf = 1
p.bf = 0
c.bf = -1
elif g.bf < 0:
p.bf = 1
c.bf = 0
else:
p.bf = 0
c.bf = 0
g.bf = 0
return g
def insert_item(self,val):
p = self.root
if not p:
self.root = AVLNode(val)
return
while True:
if p.data>val:
if p.lchild:
p = p.lchild
else:
p.lchild = AVLNode(val)
p.lchild.parent = p
node = p.lchild
break
elif p.data<val:
if p.rchild:
p = p.rchild
else:
p.rchild = AVLNode(val)
p.rchild.parent = p
node = p.rchild
break
else:
return
# 更新bf
while node.parent:
if node.parent.lchild == node: # 传递从左子树来,左边更沉
# 更新node_parent的bf-1
if node.parent.bf<0: # -1,会变为-1
# 做旋转
# 看node哪边更沉
g = node.parent.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:
node.parent.bf = 0
break
else:
node.parent.bf=-1
node = node.parent
continue
else:
# 右子树更沉同理
pass
# 链接旋转后的子树
n.parent = g
if g:
if node.parent == g.lchild:
g.lchild = n
else:
g.rchild = n
break
else:
self.root=n
break
二叉搜索树扩展应用–B树
B-Tree是一棵自平衡的多路搜索树.常用语数据库的索引.