平衡的二叉查找树——AVL
- 如何保持树的平衡,需要对每个节点跟踪一个“平衡因子”参数
- 平衡因子:左右子树的高度差
- 当每个节点的平衡因子在-1,0,1之间——平衡树
- 当有节点的平衡因子超过范围——需要重新平衡
复杂度问题:
- get方法:问题规模——总结点数,对比次数——深度
- 考虑最差情况下,左重avl,总节点数和的序列很接近fib——时间复杂度o(logn)
- put方法呢?1.需要更新一些父节点2.重新平衡最多旋转两次——时间复杂度还是o(logn)
插入新叶节点时:
- 作为右子节点插入,父节点的平衡度-1,反之+1
- 这种影响会一直传递,直到某个父节点的平衡度被调整到0,不再向上影响。
如何重新平衡——旋转:
- 右重子树左旋转,左重子树右旋转
- 举个例子,一个左重子树A,旋转之后将新根节点B(他的左子节B)的新右子节点A,但是如果原本这个新根节点B有右子节点C了, 就用A替换掉C, 把旧的右子节C改到旋转后的A的左子节点上。
如何调整平衡因子
新平衡因子和旧平衡因子之间的关系
避免死胡同
- 右重左旋转——》左重——》再旋转
- 先检查,左旋转之前,检查右子节点的因子;右旋转之前,检查左子节点的平衡度
原本赋值方法
#### 原本赋值方法
def put(self, key,val):
if self.root: # 如果是空树,直接作为root,如果不是就调用_put
self._put(key,val,self.root)
else:
self.root = TreeNode(key,val)
self.size = self.size+1
def _put(self,key,val,currentNode): # 辅助方法
if key < currentNode.key: # 大就放在右节点,但如果有了右子树,递归到下一级
if currentNode.hasleftchild():
self._put(key,val, currentNode.leftchild) # 递归
else:
currentNode.leftchild = TreeNode(key,val,parent =currentNode)
else:
if currentNode.hasrightchild():
self._put(key,val,currentNode.rightchild)
else:
currentNode.rightchild = TreeNode(key,val,parent =currentNode)
修改后的赋值方法
### 修改后的赋值方法
def _put(self,key,val,currentNode): # 辅助方法
if key < currentNode.key: # 大就放在右节点,但如果有了右子树,递归到下一级
if currentNode.hasleftchild():
self._put(key,val, currentNode.leftchild) # 递归
else:
currentNode.leftchild = TreeNode(key,val,parent =currentNode)
self.updateBalance(currentNode.leftchild)
else:
if currentNode.hasrightchild():
self._put(key,val,currentNode.rightchild)
else:
currentNode.rightchild = TreeNode(key,val,parent =currentNode)
self.updateBalance(currentNode.rightchild)
def updataBalance(self,node) : # 递归调整平衡因子的大小
if node.balanceFactor>1 or node.balanceFacotr<-1:
self.rebalance(node) # 如果不平衡了需要重新调整了
return
if not node.parent==None: # 结束条件:到了根节点
if node.isleftchild():
node.parent.balanceFactor +=1
elif node.isrightchild():
node.parent.balanceFactor -=1
if node.parent.balanceFactor!=0:
self.updateBalance(node.parent) #递归调整父节点
def rotateLeft(self,rotRoot): # 需要旋转的那个节点
newRoot= rotRoot.rightchild
rotRoot.rightchild = newRoot.leftchild
if newRoot.rightchild!=None: #如果原本有节点
newRoot.leftchild.parent = rotRoot
newRoot.parent = rotRoot.parent
if rotRoot.isRoot():
self.root = newwRoot
else :
if rotRoot.isleftchild():
rotRoot.parent.leftchild= newRoot
else:
rotBoot.parent.rightchild=newRoot
newRoot.leftchild=rotRoot
rotRoot.parent = newRoot
rotRoot.balanceFactor = rotRoot.balanceFactor+1-min(newRoot.balanceFactor,0)
newwRoot.balanceFactor =newRoot.balanceFactor+1+max(rotRoot.balanceFactor,0)
def rotateRight(self,rotRoot): # 需要旋转的那个节点
newRoot= rotRoot.leftchild
rotRoot.leftchild = newRoot.rightchild
if newRoot.leftchild!=None: #如果原本有节点
newRoot.rightchild.parent = rotRoot
newRoot.parent = rotRoot.parent
if rotRoot.isRoot():
self.root = newRoot
else :
if rotRoot.isrightchild():
rotRoot.parent.rightchild= newRoot
else:
rotBoot.parent.leftchild=newRoot
newRoot.rightchild=rotRoot
rotRoot.parent = newRoot
rotRoot.balanceFactor = rotRoot.balanceFactor+1-min(newRoot.balanceFactor,0)
newwRoot.balanceFactor =newRoot.balanceFactor+1+max(rotRoot.balanceFactor,0)
def rebalance(self,node):
if node.balanceFactor<0:
if node.rigthchild.balanceFactor>0:
### 需要先旋转右子节点再旋转本身
self.rotateRight(node.rightchild)
self.rotateLeft(node)
else: self.rotateLeft(node) # 没有问题只旋转自己
elif node.balanceFactor>0:
if node.leftchild.balanceFactor<0:
self.rotateLeft(node.leftchild)
self.rotateRight(node)
else:self.rotateRight(node)
x | 有序表 | 散列 | 二叉查找树 | avl |
---|---|---|---|---|
put | o(n) | o(1)-o(n) | o(logn)-o(n) | o(logn) |
get | o(logn) | o(1)-o(n) | o(logn)-o(n) | o(logn) |