python数据结构学习笔记-2016-12-10-01-AVL树
来源:互联网 发布:淘宝推荐系统 编辑:程序博客网 时间:2024/05/01 18:37
14.3 AVL树
AVL树由G.M.Adel‘son-Velskii和Y.M.Landis在1962年发明的自平衡二叉查找树。如果一个二叉树,其左右两子树的高度最多相差1,则称该二叉树是平衡的(balanced)。
对于AVL树中的每一个结点,都有一个平衡因子(balance factor),以表示该结点的左右两分支的高度差,平衡因子有三种状态:
- -1,表示左子树高于右子树;
- 0,表示左右两子树高度相等;
- 1,表示右子树高于左子树。
14.3.1 插入
对AVL树插入结点,初始过程与二叉查找树一致,但是在插入结点后,如果没有引起任何子树失衡,则无需调整,如:
但是如果引起了子树失衡,就需要重新调整,如:
需要调整的便是二叉查找树中的最深的结点以及离新结点最近的结点。在插入结点之后,平衡因子会在递归回调的过程中重新调整。而遇到的第一个失衡的子树,称为主结点(pivot node)。失衡的AVL树想要恢复平衡,必须要围绕主结点进行旋转(rotation)操作。
将会有四种可能情况:
- 第一种情况:主结点P的平衡因子是-1,即左分支高于右分支,而插入的结点位于左分支。要使该子树重新平衡,主结点P旋转,作为其原先左子结点C的右子结点,而C的右子结点则旋转作为P的左子结点;
- 第二种情况:此种情况涉及到主结点P,其左子结点C,以及C的右子结点G,此种情况下主结点的平衡因子仍然是-1,即左分支高于右分支,而插入结点位于C的右子树中。要想重新恢复平衡,C要向左旋转,作为G的左子结点,而主结点P要向右旋转,作为G的右子结点,即G作为新的主结点,G原来的左子结点,则作为C的右子结点,而G的右子结点则作为P的左子结点;
- 第三种情况,同第一种情况类似,只是主结点的平衡因子是1,即右分支高于左分支,且插入结点位于右分支,所需操作与第一种情况方向相反即可;
- 最后一种情况,与第四种情况类似,只是方向相反;
新平衡因子
当向树中插入新结点时,从根结点到新插入结点路径上的结点的平衡因子将会发生变化,以表示插入操作。显然,平衡因子的变化取决于结点插入前的平衡因子,以及结点插入到哪个分支。
当前平衡因子左分支右分支-1-200-11102在递归回调的过程当中,路径上失衡的结点会对相应的平衡因子进行重新平衡。在旋转操作后,受影响的结点的平衡因子将会发生相应变化。在主结点的子树进行旋转操作后,该子树的高度将减一,则主结点的所有祖先的相应分支都要减一,致使平衡因子重新回归平衡。
G原来状态新P新L新R新G情况1--00-情况2-110-0 000-0 10-1-0情况3-0-0-情况4-10-01 00-00 10-0-1
14.3.2 删除操作
同插入操作类似,删除操作开始还是运行二叉查找树的删除操作,之后再对相应子树进行平衡操作。失衡子树就是从根结点到“”真正删除结点“”的路径过程中结点,注意如果删除的是内结点时,是其逻辑后继直接覆盖在该内结点,然后再删除逻辑后继结点,真正删除的结点应该是逻辑后继结点。
但是跟插入操作不同的是,删除操作是有可能存在失衡传播的问题,即子树重新恢复平衡后,子树高度降低,造成子树的祖先仍处于失衡状态,而插入操作则能使子树重新恢复平衡后,子树的高度得到保留,使得子树的祖先的平衡因子也一并恢复平衡。所以,即便子树恢复平衡,仍然要沿着路径,检查相应结点的平衡因子,若失衡则重新平衡,直至根结点。
删除结点的情况较为复杂,尤其是只操作平衡因子,如果操作高度,则较为简单。
下面是操作平衡因子的方法:
1).按一般的二叉树删点,删除目标结点。二叉树删除特性——最终删除的点至少有一个孩子为空。
2).以移到被删除结点位置的结点P为起点(因为有哨兵叶子,所以P不会为NULL),递归向上回溯
a). 判断P是其父结点的左孩子还是右孩子:如果是左孩子则给父结点平衡因子-1,否则对平衡因子+1,代表删除结点对所在子树平衡性的影响
b). 如果父结点的平衡因子为-1或者1,说明子树的删点没有影响到以父结点为根的子树的高度,可以直接返回。 如果父结点平衡因子为0,说明不需要调整以父结点为根的子树,继续向上回溯。如果父结点平衡因子为-2或者2,则根据不同情况按上面的方法调整,然后继续向上回溯。
#-*-coding: utf-8-*-# AVL树实现的映射ADT# 平衡因子常数LEFT_HIGH = -1EQUAL_HIGH = 0RIGHT_HIGH = 1# 所有非辅助方法与二叉查找树版本类似class AVLMap(object): def __init__(self): self._root = None self._size = 0 def __len__(self): return self._size def __contains__(self, key): return self._bstSearch(self._root, key) is not None def add(self, key, value): node = self._bstSearch(key) if node is not None: node.value = value return False else: (self._root, tmp) = self._avlInsert(self._root, key, value) self._size += 1 return True def valueOf(self, key): node = self._bstSearch(self.root, key) assert node is not None, "Invalid map key." return node.value def remove(self, key): assert key in self, "Invalid map key." (self._root, tmp) = self._avlRemove(self._root, key) self._size -= 1 def __iter__(self): return _BSTMapIterator(self._root) def _bstSearch(self, subtree, target): if subtree is None: # 终止条件 return elif target < subtree.key: return self._bstSearch(subtree.left, target) elif target > subtree.key: return self._bstSearch(subtree.right, target) else: # 终止条件 return subtree # 定义左旋和右旋辅助方法,返回重新平衡后子树的根结点 def _avlRotateRight(self, pivot): C = pivot.left pivot.left = C.right C.right = pivot return C def _avlRotateLeft(self, pivot): C = pivot.right pivot.right = C.left C.left = pivot return C # 将重新恢复平衡的四种情况基于左右两分支的高度,分成两部分 def _avlLeftBalance(self, pivot): C = pivot.left if C.bfactor == LEFT_HIGH: # 首先判断C是不是左高,若是左高,则按情况1处理 pivot.bfactor = EQUAL_HIGH # 主结点的平衡因子变为0 C.bfactor = EQUAL_HIGH # 主结点的左子结点的平衡因子也变为0 pivot = _avlRotateRight(pivot) # 仅一次右旋即可 return pivot else: # 情况2 G = C.right if G.bfactor == LEFT_HIGH: # 依据主结点左子结点的右子结点的平衡因子判断 pivot.bfactor = RIGHT_HIGH C.bfactor = EQUAL_HIGH elif G.bfactor == EQUAL_HIGH: pivot.bfactor = EQUAL_HIGH C.bfactor = EQUAL_HIGH else: pivot.bfactor = EQUAL_HIGH C.bfactor = LEFT_HIGH G.bfactor = EQUAL_HIGH pivot.left = _avlRotateLeft(C) pivot = _avlRotateRight(pivot) return pivot def _avlRightBalance(self, pivot): C = pivot.right if C.bfactor == RIGHT_HIGH: # 情况3 pivot.bfactor = EQUAL_HIGH C.bfactor = EQUAL_HIGH pivot = _avlRotateLeft(pivot) return pivot else: # 情况4 G = C.left if G.bfactor == LEFT_HIGH: pivot.bfactor = RIGHT_HIGH C.bfactor = EQUAL_HIGH elif G.bfactor == EQUAL_HIGH: pivot.bfactor = EQUAL_HIGH C.bfactor = EQUAL_HIGH else: pivot.bfactor = EQUAL_HIGH C.bfactor = LEFT_HIGH G.bfactor = EQUAL_HIGH pivot.right = _avlRotateRight(C) pivot = _avlRotateLeft(pivot) return pivot # 使用递归控制向AVL树插入结点,返回元组,包括根结点的引用和子树是否更高的bool值 def _avlInsert(self, subtree, key, newitem): if subtree is None: # 空树的情形 subtree = _AVLMapNode(key, newitem) taller = True elif key == subtree.key: # ???判断该键是否已存在于树中?(有这个必要吗?_bstSearch()已经可以判断键) return (subtree, False) elif key < subtree.key: # 在subtree的左分支插入结点 (subtree, taller) = _avlInsert(subtree.left, key, newitem) # 递归 if taller: # 如果子树高度增长,对平衡因子进行调整 if subtree.bfactor == LEFT_HIGH: subtree = _avlLeftBalance(subtree) # 在旋转函数中,就已经将相应结点的平衡因子作出了调整 taller = False # 此时增高为False elif subtree.bfactor == EQUAL_HIGH: subtree.bfactor = LEFT_HIGH taller = True else: subtree.bfactor = EQUAL_HIGH taller = False elif key > subtree.key: # 同上 (subtree, taller) = _avlInsert(subtree.right, key, newitem) if taller: if subtree.bfactor == LEFT_HIGH: subtree.bfactor = EQUAL_HIGH taller = False elif subtree.bfactor == EQUAL_HIGH: subtree.bfactor = RIGHT_HIGH taller = True else: subtree = _avlRightBalance(subtree) taller = False return (subtree, taller) # 在AVL树中删除结点 def _avlRemove(self, subtree, key): if subtree is None: return subtree elif key < subtree.key: parent = subtree subtree = _avlRemove(subtree.left, key) parent.bfactor += 1 # 在左子树中删除结点,根结点的平衡因子加一 if parent.bfactor == 2: # 失衡则进行旋转 parent = self._avlRightBalance(parent) elif key > subtree.key: parent = subtree subtree = _avlRemove(subtree.right, key) parent.bfactor -= 1 # 在右子树中删除结点,根结点的平衡因子减一 if parent.bfactor -= -2: # 失衡则进行旋转 parent = self._avlLeftBalance(parent) else: if subtree.left is None and subtree.right is None: return subtree elif subtree.left is None or subtree.right is None: # 删除的结点是只有一个子结点的内结点 if subtree.left is not None: return subtree.left else: return subtree.right else: successor = self._bstMinumum(subtree.right) # 搜索目标结点的逻辑后继 subtree.key = successor.key # 逻辑后继替代目标结点 subtree.value = successor.value subtree.right = self._bstRemove(subtree.right, successor.key) # 删除原逻辑后继 return subtreeclass _BSTMapIterator(object): def __init__(self, root): self._theStack = Stack() self._traverseToMinNode(root) # 将二叉搜索树的元素压入栈中,此时不是所有元素已压入栈中 def __iter__(self): return self def next(self): if self._theStack.isEmpty(): raise StopIteration else: node = self._theStack.pop() key = node.key if node.right is not None: # 将该结点的逻辑后继压入栈中 self._traverseToMinNode(node.right) # 对二叉搜索树进行遍历,直到找到其最小值为止,期间的元素压入栈中 def _traverseToMinNode(self, subtree): if subtree is not None: subtree._theStack.push(subtree) subtree._traverseToMinNode(subtree.left)class _AVLMapNode(object): def __init__(self, key, value): self.key = key self.value = value self.bfactor = EQUAL_HIGH self.left = None self.right = None
0 0
- python数据结构学习笔记-2016-12-10-01-AVL树
- 数据结构学习笔记--AVL树
- 数据结构学习笔记:B树、B+树、红黑树、AVL树
- 数据结构与算法分析学习笔记--第四章AVL树
- 学习笔记 AVL树
- python数据结构学习笔记-2016-12-03-01-堆排序
- python数据结构学习笔记-2016-12-04-01-Morse电码
- python数据结构学习笔记-2016-10-15-01-矩阵ADT
- python数据结构学习笔记-2016-10-17-01-集合
- python数据结构学习笔记-2016-10-21-01-复杂度分析
- python数据结构学习笔记-2016-10-23-01-搜索
- python数据结构学习笔记-2016-10-24-01-排序列表
- python数据结构学习笔记-2016-10-27-01-链表
- python数据结构学习笔记-2016-11-27-01-二叉树
- python数据结构学习笔记-2016-11-28-01-表达式树
- AVL树学习笔记&模板
- AVL树的学习笔记
- python数据结构学习笔记-2016-10-14-02-python列表
- 安装Eclipse Maven插件的几种方法
- 性能测试-LoadRunner初级
- 文章标题
- SSH协议基础
- 用HTML加CSS模仿的W3school的主页
- python数据结构学习笔记-2016-12-10-01-AVL树
- Android 渗透测试学习手册 第四章 对 Android 设备进行流量分析
- IntelliJ IDEA导航特性Top20
- 字符串的插入
- 接口测试-初级学习
- 字符串比较
- HDU2546饭卡
- Android实现硬盘缓存
- Linux锁机制