数据结构与算法Python版 平衡二叉查找树AVL

embedded/2024/12/28 11:41:55/

文章目录


一、平衡二叉查找树

平衡二叉查找树-AVL树的定义

  • AVL树:在key插入时一直保持平衡的二叉查找树。
  • 可以利用AVL树实现抽象数据类型映射Map。与二叉查找树相比,AVL树基本上与二叉查找树的实现相同,不同在于生成与维护过程。
  • 平衡因子balance factor:根据节点的左右子树的高度来定义,等于左子树的高度减去右子树的高度
    • 如果平衡因子大于0,称为“左重left-heavy”,
    • 如果平衡因子小于0,称为“右重right-heavy”
    • 如果平衡因子等于0,称为平衡
  • 如果一个二叉查找树中每个节点的平衡因子都在-1,0,1之间,则把这个二叉查找树称为平衡树
  • AVL树的搜索时间复杂度为O(log2n)

AVL树的Python实现

  • _put方法:AVL树同时也是二叉查找树,所以新key必定以叶节点形式插入到AVL树中。插入一个新key后,更新着其父节点到根节点的平衡因子,对平衡因子大于或小于1的节点进行重新平衡

  • rebalance重新平衡方法:将不平衡的子树进行旋转rotation,右重子树进行左旋转,左重子树进行右旋转。以右重子树进行左旋转为例,分两种情况:

    • 情况一:将右子节点B提升为子树的根,将旧根节点A作为新根节点B的左子节点。如果新根节点B原来有左子节点,则将此节点设置为A的右子节点(A的右子节点一定有空)。
      在这里插入图片描述

    • 情况二:在左旋转之前检查右子节点的因子。如果右子节点“左重”的话,先对它进行右旋转,再实施原来的左旋转
      在这里插入图片描述

python">from my_tree import TreeNode, BinarySearchTreeclass AVLNode(TreeNode):def __init__(self, key, val, l_child=None, r_child=None, parent=None, b_factor=0):self.key = keyself.val = valself.l_child: AVLNode = l_childself.r_child: AVLNode = r_childself.parent: AVLNode = parentself.b_factor = b_factorclass AVLTree(BinarySearchTree):def put(self, key, val):"""插入key val,构造BST"""# 如果树不为空时,调用_put()插入到BST;否则成为BST的根if self.root:self._put(key, val, self.root)else:self.root = AVLNode(key, val)self.size += 1def _put(self, key, val, cur_node: TreeNode):# 如果key比currentNode小,那么_put到左子树;否则,_put到左子树。if key < cur_node.key:if cur_node.has_l_child():self._put(key, val, cur_node.l_child)else:cur_node.l_child = AVLNode(key, val, parent=cur_node)# 增加:调整因子self.update_balance(cur_node.l_child)else:if cur_node.has_r_child():self._put(key, val, cur_node.r_child)else:cur_node.r_child = AVLNode(key, val, parent=cur_node)# 增加:调整因子self.update_balance(cur_node.r_child)def update_balance(self, node: AVLNode):if node.b_factor > 1 or node.b_factor < -1:# 重新平衡self.re_balance(node)returnif node.parent != None:if node.is_l_child():node.parent.b_factor += 1elif node.is_r_child():node.parent.b_factor -= 1# 递归调用:调整父节点因子if node.parent.b_factor != 0:self.update_balance(node.parent)def re_balance(self, node: AVLNode):if node.b_factor < 0:  # 右重需要左旋if node.r_child.b_factor > 0:self.rotate_right(node.r_child)  # 右子节点先右旋self.rotate_left(node)else:self.rotate_left(node)elif node.b_factor > 0:if node.l_child.b_factor < 0:self.rotate_left(node.l_child)self.rotate_right(node)else:self.rotate_right(node)def rotate_left(self, rot_root: AVLNode):new_root = rot_root.r_childrot_root.r_child = new_root.l_childif new_root.l_child != None:new_root.l_child.parent = rot_rootnew_root.parent = rot_root.parentif rot_root.is_root():self.root = new_rootelse:if rot_root.is_l_child():rot_root.parent.l_child = new_rootelse:rot_root.parent.r_child = new_rootnew_root.l_child = rot_rootrot_root.parent = new_rootrot_root.b_factor = rot_root.b_factor + 1 - min(new_root.b_factor, 0)new_root.b_factor = new_root.b_factor + 1 + max(rot_root.b_factor, 0)def rotate_right(self, rot_root: AVLNode):new_root = rot_root.l_childrot_root.l_child = new_root.r_childif new_root.r_child != None:new_root.r_child.parent = rot_rootnew_root.parent = rot_root.parentif rot_root.is_root():self.root = new_rootelse:if rot_root.is_r_child():rot_root.parent.r_child = new_rootelse:rot_root.parent.r_child = new_rootnew_root.r_child = rot_rootrot_root.parent = new_rootrot_root.b_factor = rot_root.b_factor + 1 - min(new_root.b_factor, 0)new_root.b_factor = new_root.b_factor + 1 + max(rot_root.b_factor, 0)

AVL_148">二、AVL树测试

在这里插入图片描述

在这里插入图片描述

  • 参考资料:AVL树在线演示。在线演示网站,在删除节点时使用的是中序前驱(左子树中的最大节点),所示演示结果与输出结果有差异。
python">bst = AVLTree()
bst[17] = "tiger"
bst[26] = "dog"
bst[31] = "cow"
bst[54] = "cat"
bst[93] = "lion"
bst[77] = "bird"bst.display_tree()
print(bst[26])
del bst[26]
print(bst[26])
bst.display_tree()### 输出结果
54 26 17 31 93 77 
dog
None
54 31 17 93 77

AVL_193">三、AVL树-算法分析

  • AVL树始终维持平衡,get方法始终保持O(log2n)最佳性能
  • AVL树的put方法分为两个部分:
    • 需要插入的新节点是叶节点,更新其所有父节点和祖先节点的代价最多为O(log2n)。
    • 如果插入的新节点引发了不平衡,重新平衡最多需要2次旋转,但旋转的代价与问题规模无关,是常数O(1)
    • put方法的时间复杂度还是O(log2n)

映射的实现方法小结

  • 抽象数据类型映射Map,可以采用多种数据结构算法来实现,时间复杂度数量级如下
操作类型有序表散列表二叉查找树AVL
putO(n)O(1) -> O(n)O(log2n) -> O(n)O(log2n)
getO(log2n)O(1) -> O(n)O(log2n) -> O(n)O(log2n)
inO(log2n)O(1) -> O(n)O(log2n) -> O(n)O(log2n)
delO(n)O(1) -> O(n)O(log2n) -> O(n)O(log2n)

您正在阅读的是《数据结构算法Python版》专栏!关注不迷路~


http://www.ppmy.cn/embedded/149430.html

相关文章

《异构计算:多元算力聚变,点燃高性能计算新引擎 – CPU、GPU与FPGA算力融合》

数字化浪潮澎湃之际&#xff0c;算力需求呈指数级攀升态势&#xff0c;数据中心亦随之踏上向计算中心深度蜕变之旅&#xff0c;算力作为新兴生产力要素&#xff0c;正重塑产业格局。多元数据形态与丰富场景交相辉映&#xff0c;强力驱动异构计算步入高速发展快车道。 置身 AI 与…

“智能控制的新纪元:2025年机器学习与控制工程国际会议引领变革

ICMLCE 2025 | 机器学习与控制工程国际会议 ✨宝子们&#xff0c;今天要为大家介绍的是一个在机器学习和控制工程领域备受瞩目的国际学术盛会——2025年机器学习与控制工程国际会议&#xff08;ICMLCE 2025&#xff09;。本次大会将在美丽的大理举行&#xff0c;旨在汇聚全球顶…

力扣矩阵-算法模版总结

lc-73.矩阵置零-(时隔14天)-12.27 思路&#xff1a;(23min22s) 1.直接遍历遇0将行列设0肯定不行&#xff0c;会影响后续判断&#xff0c;题目又要求原地算法&#xff0c;那么进一步考虑是否可以将元素为0&#xff0c;其行列需要设为0的位置给存储下来&#xff0c;最后再遍历根据…

Git快速查阅

根据平时使用git的流程来编写。 git init 建立当前目录git仓库 git add . 将当前目录所有文件加入git暂存区&#xff0c;让git进行管理 git status 查看当前暂存区内容 git commit -m commit 描述 将当前暂存区中的内容进行提交&#xff0c;保存为一个commit。 git log 查…

嵌入式硬件面试题

1、请问什么是通孔、盲孔和埋孔&#xff1f;孔径多大可以做机械孔&#xff0c;孔径多小必须做激光孔&#xff1f;请问激光微型孔可以直接打在元件焊盘上吗&#xff0c;为什么&#xff1f; 通孔是贯穿整个PCB的过孔&#xff0c;盲孔是从PCB表层连接到内层的过孔&#xff0c;埋孔…

Matlab个性化绘图第7期—带标记面的三维多组折线图

上一期文章分享了Matlab带标记面的三维折线图&#xff1a; 进一步&#xff0c;再来分享一下带标记面的三维多组折线图。 由于Matlab未收录带标记面的三维多组折线图的绘图函数&#xff0c;因此需要大家自行解决。 本文使用自制的addPlane小工具进行带标记面的三维多组折线图的…

快速排序算法

一、快速排序简介 **快速排序&#xff08;Quick Sort&#xff09;**是一种基于分治法的高效排序算法&#xff0c;由C. A. R. Hoare于1960年提出。它的平均时间复杂度为O(n log n)&#xff0c;在实际应用中&#xff0c;由于其优秀的性能和较高的效率&#xff0c;被认为是排序算…

LLMs之o3:《Deliberative Alignment: Reasoning Enables Safer Language Models》翻译与解读

LLMs之o3&#xff1a;《Deliberative Alignment: Reasoning Enables Safer Language Models》翻译与解读 导读&#xff1a;2024年12月&#xff0c;这篇论文提出了一种名为“审慎式对齐 (Deliberative Alignment)”的新方法&#xff0c;旨在提高大型语言模型 (LLM) 的安全性。论…