目录
1 二叉排序树
对一个二叉树中序遍历能得到一个有序序列的二叉树即为二叉排序树
2 平衡因子
二叉树上的节点左右子树的深度之差为平衡因子,记为BF(Blance Factor)
3 平衡二叉树
一个二叉排序树中各节点的左右子树高度差不超过1,也即每个节点的平很因子BF为0、-1或1
4 最小不平衡子树
插入一个节点,往上寻找“祖辈节点”,最近的BF绝对值大于1 的节点为根的子树为最小不平衡子树
5 平衡二叉树实现原理
当插入节点导致平衡二叉树的平衡性被破坏(存在|BF|>1的节点),保持二叉排序树特性,通过对最小不平衡子树进行左旋或右旋,使之称为新的平衡子树从而整个二叉树重归平衡
6 旋转
6.1 右旋(LL型)
原因:由于对p的左孩子的左子树插入,导致平衡性被破坏
解决:在最小不平衡子树中,根结点变为左孩子的右孩子,新根结点原来的的右孩子(如果有)给旧的的根节点当左孩子
LL型两种基本最小不平衡子树及其旋转过程:
(1)该最小不平衡子树的根结点为6,新插入结点为1
(2)该最小不平衡子树的根结点为6,新插入结点为2
6.2 左旋(RR型)
原因:由于对p的右孩子的右子树插入,导致平衡性被破坏
解决:在最小不平衡子树中,根节点变为右孩子的左孩子,新根节点原来的的左孩子(如果有)给旧的的根节点当右孩子
RR型两种基本最小不平衡子树及其旋转过程:
(1)该最小不平衡子树的根结点为6,新插入结点为8
(2)该最小不平衡子树的根结点为2,新插入结点为6
6.3 LR 型
原因:由于对p的左孩子的右子树插入,导致平衡性被破坏
解决:根节点的左孩子先左旋,然后再对调整后的根节点右旋。
LR型两种基本最小不平衡子树及其旋转过程:
(1)该最小不平衡子树的根结点为6,新插入结点为5
(2)该最小不平衡子树的根结点为6,新插入结点为5
6.4 RL型
原因:由于对p的右孩子的左子树插入,导致平衡性被破坏
解决:根节点的右孩子先右旋,然后再对调整后的根节点左旋。
RL型两种基本最小不平衡子树及其旋转过程:
(1)该最小不平衡子树的根结点为6,新插入结点为5
(2)该最小不平衡子树的根结点为4,新插入结点为5
7 代码
7.1 关于旋转后bf值的更新
假设:
- 最小不平衡子树中,受插入结点影响bf值改变的祖辈结点有3个或2个,算上插入结点,分别记为(1)node、g、c、f和(2)g(也即node插作g)、c、f。
观察可得:
- LL型和RR型的p结点和c结点的bf值最终变都变为0,其他结点的bf值自更新后(插入结点后向上更新bf值,找最小不平衡子树)便不变。
- LR型和RL型中:
- 若为(2)类
- 则p.bf - > 0, c.bf -> 0
- 若为(1)类,
- LR型中新结点插作g的左孩子则g.bf -> 0, c.bf -> 0, p.b f-> -1
- LR型中新结点插作g的右孩子则g.bf -> 0, c.bf -> 1, p.b f-> 0
- RL型中新结点插作g的左孩子则g.bf -> 0, c.bf -> -1, p.bf -> 0
- RL型中新结点插作g的右孩子则g.bf -> 0, c.bf -> 0, p.bf -> 1
7.1 AVLT代码
class AVLT:
class Node:
def __init__(self,data):
self.data = data
self.lchild = None
self.rchild = None
self.parent = None
self.bf = 0
def __init__(self, _list=[]):
self.root = None
if _list: # 根据提供的列表中结点值来初始化树
for v in _list:
self.insert_no_rec(v)
7.2 旋转代码
def LLSpin(self, p, c):
"""LL型:
根结点变作左孩子的右孩子,新根结点原来的的右孩子(如果有)给旧的的根结点当左孩子;
c 为最小不平衡子树根节点的左孩子, p为最小不平衡子树的根节点"""
c.parent = p.parent #新根结点c继承旧根结点p的父亲
if p.parent:# 先判断,旧根结点不是整树的根结点
# 再判断旧根结点是其原父亲结点的左孩子还是右孩子,让新根结点继承
if p.parent.lchild == p:
p.parent.lchild = c
elif p.parent.rchild == p:
p.parent.rchild = c
else: # 如果旧根结点p为整树的根,则要更新一下self.root
self.root = c
p.lchild = c.rchild # p的lchild不再是c了,c如果有右孩子,p就指向该右孩子,没有就指向None
if c.rchild: # c如果有右孩子,给p当左孩子
c.rchild.parent = p
# p得到c的右孩子后再给c当右孩子
c.rchild = p
p.parent = c
# 更新bf值
p.bf = 0
c.bf = 0
return c
def RRSpin(self, p, c):
"""RR型:
根节点变为右孩子的左孩子,新根节点原来的的左孩子(如果有)给旧的的根节点当右孩子
c 为最小不平衡子树根节点的右孩子, p为最小不平衡子树的根节点"""
c.parent = p.parent #新根结点c继承旧根结点p的父亲
if p.parent:# 先判断,旧根结点不是整树的根结点
# 再判断旧根结点是其原父亲结点的左孩子还是右孩子,让新根结点继承
if p.parent.lchild == p:
p.parent.lchild = c
elif p.parent.rchild == p:
p.parent.rchild = c
else: # 如果旧根结点p为整树的根则,要更新一下self.root
self.root = c
p.rchild = c.lchild # p的rchild再是c了,c如果有左孩子,p就指向该左孩子,没有就指向None
if c.lchild: # c如果有左孩子,给p当右孩子
c.lchild.parent = p
p.bf = 0
c.bf = 0
# p得到c的左孩子后再给c当左孩子
c.lchild = p
p.parent = c
# 更新bf值
p.bf = 0
c.bf = 0
return c
def LRSpin(self, p, c):
"""LR型:根节点的左孩子先左旋,然后再对调整后的根节点右旋"""
g = c.rchild
self.RRSpin(c,g)
self.LLSpin(p,g)
# 更新bf
if g.bf > 0: # 插入作g左孩子
# 旋转后
p.bf = -1
c.bf = 0
g.bf = 0
elif g.bf < 0: # 插入作g右孩子
# 旋转后
p.bf = 0
c.bf = 1
g.bf = 0
elif g.bf == 0: # 插入作为g
p.bf = 0
c.bf = 0
return g
def RLSpin(self, p, c):
"""RL型:根节点的右孩子先右旋,然后再对调整后的根节点左旋"""
g = c.lchild
self.LLSpin(c,g)
self.RRSpin(p,g)
# 更新bf
if g.bf > 0:# 插入作g左孩子
# 旋转后
p.bf = 0
c.bf = -1
g.bf = 0
elif g.bf < 0:# 插入作g右孩子
# 旋转后
p.bf = 1
c.bf = 0
g.bf = 0
elif g.bf == 0:# 插入作为g
p.bf = 0
c.bf = 0
return g
7.3 插入代码
def insert_no_rec(self, val):
""" 1. 若插作根结点,直接赋值并返回,且不必维护bf
2. 若插作非根结点,根据二叉排序树特性,插入
3. 插入结点后,若要维护bf,则从插入结点往上维护他的父亲或父亲的父亲,维护到新根结点
4. 关于维护bf,当维护到新根结点时,不必再往上维护,只需调用旋转函数,该函数内部会再对整个不平衡子树的关键结点的bf进行维护
"""
par = self.root
if not par:
self.root = AVLT.Node(val)
return
while True:
if val < par.data:
if par.lchild:
par = par.lchild
else:
par.lchild = AVLT.Node(val)
par.lchild.parent = par
node = par.lchild # node即为插入的结点
break
elif val > par.data:
if par.rchild:
par = par.rchild
else:
par.rchild = AVLT.Node(val)
par.rchild.parent = par
node = par.rchild
break
elif val == par.data:
return # -*-本例中默认不重复关键字(结点的值)-*-
# 2.寻找最小不平衡子树并做旋转
"""思想:从新插入的结点向上更新他的祖辈结点的bf值,直到发现bf绝对值为2或没有发现bf绝对值为2的祖辈结点"""
while node.parent: # 向上更新
"""解决最小不平衡子树的旋转类型受新结点插入在左子树还是右子树的影响"""
if node.parent.lchild == node:
if node.parent.bf == 0: # 1.刚插入时 2.插入后第二次循环,node指向g结点
node.parent.bf = 1 # 插入导致左比右深1
node = node.parent # node指向父节点
elif node.parent.bf == -1: # 插入后父亲结点bf平衡,不会影响整体平衡性
node.parent.bf = 0
break
elif node.parent.bf == 1: # node指向c结点
if node.bf == 1: # LL型
p = node.parent
c = node
n = self.LLSpin(p, c) # node.parent 为结点p, node为结点c
elif node.bf == -1: # LR型
p = node.parent
c = node
n = self.LRSpin(p, c)
elif node.parent.rchild == node:
if node.parent.bf == 0: # 1.刚插入时 2.插入后第二次循环,node指向g结点
node.parent.bf = -1 # 插入导致右比左深1
node = node.parent # node指向父节点
elif node.parent.bf == 1: # 插入后父亲结点bf平衡,不会影响整体平衡性,无须继续往上走
node.parent.bf = 0
break
elif node.parent.bf == -1: #node指向c结点
if node.bf == -1: # RR型
p = node.parent
c = node
n = self.RRSpin(p.data, c.data)
elif node.bf == 1: # RL型
p = node.parent
c = node
n = self.RLSpin(p, c)
8 前序、中序、后序、层序遍历实现
def pre_order(self, root):
"""前序遍历"""
order_list = []
def order(root):
if root:
order_list.append(root.data)
order(root.lchild)
order(root.rchild)
order(root)
return order_list
def in_order(self, root):
"""中序遍历"""
order_list = []
def order(root):
if root:
order(root.lchild)
order_list.append(root.data)
order(root.rchild)
order(root)
return order_list
def post_order(self, root):
"""后序遍历"""
order_list = []
def order(root):
if root:
order(root.lchild)
order(root.rchild)
order_list.append(root.data)
order(root)
return order_list
def level_order(self, root):
"""层序遍历"""
from collections import deque
order_list = []
def order(root):
dq = deque()
dq.append(root)
while len(dq) > 0:
node = dq.popleft()
order_list.append(node.data)
if node.lchild:
dq.append(node.lchild)
if node.rchild:
dq.append(node.rchild)
order(root)
return order_list