【Python数据结构】——二叉平衡树AVL(查找、构建、删除、插入、打印、遍历)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2021/7/28 20:57
# @Author  : @linlianqin
# @Site    : 
# @File    : 二叉平衡树专题(创建、插入、查找).py
# @Software: PyCharm
# @description:'''二叉平衡树的特点:在二叉查找树的基础上对每一个节点的左右子树的高度进行了规定——每一个节点的左右子树的高度差不超过1
1)任何一个节点都有:左子树节点值 <= 节点值 < 右子树节点值
2)任何一个节点的左子树高度和右子树高度之差不超过1两个概念:
1)当前节点的高度 = max(左子树节点高度,右子树节点高度) + 1
2)平衡因子 = 左子树节点高度 - 右子树节点高度调式代码过程中出现的问题:
1)高度值混淆:高度值规定——空树高度为0,默认节点高度值为1,叶节点的高度值默认为1;
2)注意插入方法和二叉查找树的插入方法的区别:
BST的插入过程中,头节点是不会发生改变的,因为只需要满足左<=中<右即可;
AVL的插入过程中,由于要维持树的平衡,同时要满足BST的性质,因此再插入过程中,各个子树的根节点是会发生改变的,因此插入结束后,需要将最新的头节点返回,以便后续的遍历
3)在更新节点高度值的时候,设计更新节点高度值函数的时候没有更新节点的高度值,而是直接返回了,
导致节点高度值出现错误
错误代码:return max(self.getTreeHeight(root.left),self.getTreeHeight(root.right))+1
正确代码:root.height = max(self.getTreeHeight(root.left),self.getTreeHeight(root.right))+1
4)插入元素后,在进行左旋\右旋的时候,没有接受返回来的调整平衡后的子树根节点,导致最后遍历AVL树的时候元素不完整
错误代码:self.AVL_L(root.left)
正确代码:root.left = self.AVL_L(root.left)'''# 定义二叉平衡树的节点类,比普通的二叉树节点类多了一个高度属性
class TreeNode:def __init__(self,val,left=None,right=None,height=1):self.val = valself.left  = leftself.right = rightself.height = height# 二叉平衡树的基本操作
class AVL_ops:# 计算以当前节点为根节点的树的高度def getTreeHeight(self,root):if root == None:return 0return root.height# 更新以当前节点为根节点的树的高度 = max(左子树节点高度,右子树节点高度) + 1def updateTreeHeight(self,root):## 这里不需要判断左右节点是否为空,是因为在self.getTreeHeight函数中已经将空节点的高度设置为0了root.height = max(self.getTreeHeight(root.left),self.getTreeHeight(root.right))+1# 计算当前节点的平衡因子 = (左子树节点高度 - 右子树节点高度)def getNodeBalanceFactor(self,root):return self.getTreeHeight(root.left)- self.getTreeHeight(root.right)# 在二叉平衡树中的查找目标值—-因为AVL实际上是BST因此查找也是一样的def AVL_search(self,root,target):# 访问到了空树的时候,说明目标值不存在if root is None:return False# 相等说明目标值存在if target == root.val:return True# 目标值在当前节点的左子树上if target < root.val:return self.AVL_search(root.left,target)# 目标值在当前节点的右子树上if target > root.val:return self.AVL_search(root.right,target)# 在二叉平衡树中进行插入——和BST的插入有所区别,这里是因为需要保证树的高度处于平衡状态## 插入一个元素后,高度失去平衡的节点的平衡因子一定是2或者-2只需要将这里的失衡的节点调节就可以## 事实证明:只需要将失衡的节点进行调节整棵树就可以达到平衡状态## 这里分为左旋和右旋两种方式进行平衡调节,对于失衡的树有LL,LR,RR,RL四种结构## 其中LL右旋达到平衡、LR先左节点左旋为LL再右旋、RR左旋达到平衡、RL先右节点右旋为RR然后再达到平衡### 左旋:def AVL_L(self,root):# 1)临时节点存放根节点的右节点temp = root.right# 2)将根节点的右节点更新为temp的左节点root.right = temp.left# 3)将temp节点的左节点更新为根节点temp.left = root# 4)更新temp和root的高度值self.updateTreeHeight(root)self.updateTreeHeight(temp)# 5)更新根节点root = tempreturn root### 右旋:和左旋相反def AVL_R(self,root):# 1)临时节点存放根节点的左节点temp = root.left# 2)将根节点的左节点更新为temp的右节点root.left = temp.right# 3)将temp节点的右节点更新为根节点temp.right = root# 4)更新temp和root的高度值self.updateTreeHeight(root)self.updateTreeHeight(temp)# 5)更新根节点root = tempreturn root# 插入的话首先需要进行失衡节点处的树型判断LL\LR\RR\RL# 步骤:# 1)先将元素按照左小右大的性质插入到对应叶节点末尾;# 2)再更新插入元素后的各节点高度;# 3)根据各节点高度值计算各节点的平衡因子;# 4)根据节点及其左右子节点的平衡因子来判断树形;# 5)根据树形选择左旋、右旋组合来将插入后的失衡二叉树调整至平衡二叉树AVL# 6)返回最新的根节点完成插入操作def AVL_insert(self,root,target):if root is None:return TreeNode(target)# target插入到左子树,说明左子树会出现失衡,树形可能为LL或者LRif root.val > target:root.left = self.AVL_insert(root.left,target)# 更新树高self.updateTreeHeight(root)# 判断树形if self.getNodeBalanceFactor(root) == 2:# LL型,右旋if self.getNodeBalanceFactor(root.left) == 1:root = self.AVL_R(root)# LR型,右旋elif self.getNodeBalanceFactor(root.left) == -1:root.left = self.AVL_L(root.left)root = self.AVL_R(root)# target插入到右子树,说明右子树会出现失衡,树形可能为RR或者RLelse:root.right = self.AVL_insert(root.right, target)# 更新树高self.updateTreeHeight(root)# 判断树形if self.getNodeBalanceFactor(root) == -2:# RR型,左旋if self.getNodeBalanceFactor(root.right) == -1:root = self.AVL_L(root)# RL型,先对右节点右旋再对根节点左旋elif self.getNodeBalanceFactor(root.right) == 1:root.right = self.AVL_R(root.right)root = self.AVL_L(root)return root# todo:删除元素,其实就是插入的反向操作def AVL_delete(self,root,target):pass# AVL创建——其实就是一个一个插入元素def AVL_create(self,nums):if len(nums) == 0:return Noneroot = TreeNode(nums[0])for num in nums[1:]:root = self.AVL_insert(root,num)return root# 层序遍历AVLdef AVL_layer(self,root):if root is None:returnq = [root]Height = []while q:newQ = []for node in q:print(node.val,end=',')Height.append(node.height)if node.left is not None:newQ.append(node.left)if node.right is not None:newQ.append(node.right)q = newQprint("\n对应节点的高度")print(Height)# 中序# 遍历二叉查找数,中序遍历def AVL_mid_scan(self,root):if root is None:return# 遍历左子树self.AVL_mid_scan(root.left)# 遍历根节点print(root.val, end=',')self.AVL_mid_scan(root.right)li = [1,2,3,4,5,6,7]
print("原始列表:",li)# 创建
print("创建AVL")
root = AVL_ops().AVL_create(li)
# 层序遍历
print("层序遍历")
AVL_ops().AVL_layer(root)
print("中序遍历")
AVL_ops().AVL_mid_scan(root)print("\n插入数值-1")
root = AVL_ops().AVL_insert(root,-1)
print("插入数值-1后的层序遍历")
AVL_ops().AVL_layer(root)
print("插入数值-1后的中序遍历")
AVL_ops().AVL_mid_scan(root)print("\n插入数值5")
root = AVL_ops().AVL_insert(root,-1)
print("插入数值5后的层序遍历")
AVL_ops().AVL_layer(root)
print("插入数值5后的中序遍历")
AVL_ops().AVL_mid_scan(root)

示例结果:

原始列表: [1, 2, 3, 4, 5, 6, 7]
创建AVL
层序遍历
4,2,6,1,3,5,7,
对应节点的高度
[3, 2, 2, 1, 1, 1, 1]
中序遍历
1,2,3,4,5,6,7,
插入数值-1
插入数值-1后的层序遍历
4,2,6,1,3,5,7,-1,
对应节点的高度
[4, 3, 2, 2, 1, 1, 1, 1]
插入数值-1后的中序遍历
-1,1,2,3,4,5,6,7,
插入数值5
插入数值5后的层序遍历
4,2,6,-1,3,5,7,-1,1,
对应节点的高度
[4, 3, 2, 2, 1, 1, 1, 1, 1]
插入数值5后的中序遍历
-1,-1,1,2,3,4,5,6,7,

注意和二叉查找树的细节区别,细节区别见本文代码前部分《遇到的问题》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/255893.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

随笔速记

LVM增加与缩小Swap分区操作 http://blog.sina.com.cn/s/blog_5f2ca1ed0101ebw8.html Ubuntu删除多余内核 # dpkg --get-selections | grep linux # apt-get purge linux-headers-3.0.0-12 linux-image-3.0.0-12-generic # update-grub Ubuntu清理安装包、已卸载软件、已卸载软件…

【测试开发】测试用例讲解

文章目录 目录 文章目录 前言 一、测试用例的基本要素 二、测试用例的设计方法 1.基于需求的设计方法 对日历根据web界面的功能布局分析出的功能框图如下&#xff1a; 继续举一个例子百度云盘非功能测试的案例&#xff1a; 2.等价类 3.边界值 5.正交表 6.场景设计法 7…

Linux下进行Web服务器压力(并发)测试工具http_load、webbench、ab、Siege、autobench简单使用教程(转)...

一、http_load 程序非常小&#xff0c;解压后也不到100K http_load以并行复用的方式运行&#xff0c;用以测试web服务器的吞吐量与负载。但是它不同于大多数压力测试工 具&#xff0c;它可以以一个单一的进程运行&#xff0c;一般不会把客户机搞死。还可以测试HTTPS类的网站请求…

【Python数据结构】——并查集的实现(查找、合并、集合、实例)

#!/usr/bin/env python # -*- coding: utf-8 -*- # Time : 2021/7/30 23:12 # Author : linlianqin # Site : # File : 并查集专题&#xff08;合并、查找、集合&#xff09;.py # Software: PyCharm # description: 并查集其实就是多个数组&#xff0c;每一个数组都…

如何实现ABB机器人与老式焊机的连接控制

问题&#xff1a; 请教一个机器人与老式焊机如何连接&#xff0c;如何设置。 我现在是用SET指令设DO为1再外接继电器来控制焊机工作的&#xff0c;用RESET指令来使焊机停止工作的。现在可 以焊接&#xff0c;但是如果中间停止或机器人报错停止不动&#xff0c;焊机始终处于工作…

gitlab 杂记

GitLab 编译部署 1&#xff0c;请尽量不要在国内主机上部署&#xff0c;中途天朝很有可能导致gem执行出现问题&#xff0c;以下在AWS上部署&#xff1b; 2&#xff0c;系统中必须要有swap分区&#xff0c;不然会出现500错误&#xff1b; 系统版本&#xff1a;CentOS 6.x x86_6…

Hadoop分布式系统的安装部署

1、关于虚拟机的复制 新建一台虚拟机&#xff0c;系统为CentOS7&#xff0c;再克隆两台&#xff0c;组成一个三台机器的小集群。正常情况下一般需要五台机器&#xff08;一个Name节点&#xff0c;一个SecondName节点&#xff0c;三个Data节点。&#xff09; 此外&#xff0c;为…

Windows线程调度学习(一)

前言 Windows 线程调度器的实现分散在内核各处&#xff0c;并且与许多组件都有关联&#xff0c;很难进行系统地学习&#xff0c;所以我打算写几篇文章来记录下自己学习过程中的思考和分析&#xff0c;同时也方便日后查阅&#xff0c;此文可以看作是《Windows内核原理与实现》中…

机器人的有效负荷

问题&#xff1a; 假如我想在程序里做多几个有效载荷,但在手动操纵画面上只能加一个,其它要怎样用?给个实际例子给我啊. 回答&#xff1a; 在搬运中&#xff0c;确实是有载荷发生变化的情况&#xff0c;如两抓(A B)的夹具&#xff0c;有三种载荷情况&#xff0c;1、A抓有载荷…

【Python生成readme文件】——Markdown语法

链接&#xff1a;https://www.cnblogs.com/wj-1314/p/8547763.html

编程之美2.13子数组的最大乘积

题目&#xff1a; 给定一个长度为N的数组&#xff0c;只许用乘法&#xff0c;不许用除法&#xff0c;计算任意&#xff08;N-1&#xff09;个数的组合中乘积最大的一个组&#xff0c;并写出算法的时间复杂度。 如果把所可能的乘积找出来&#xff0c;共有&#xff08;N-1&#x…

[SceneKit专题]11-Reference-Nodes引用节点

说明 本系列文章是对<3D Apple Games by Tutorials>一书的学习记录和体会 此书对应的代码地址 SceneKit系列文章目录 本文将完成一个完整的node节点,带有完整贴图,并将其导入其他场景中,成为其中的一个引用节点.这样可以更方便的组织场景,并能复用场景中的节点,正类似于面…

scapy 安装及简单测试

关于scapy Scapy的是一个强大的交互式数据包处理程序&#xff08;使用python编写&#xff09;。它能够伪造或者解码大量的网络协议数据包&#xff0c;能够发送、捕捉、匹配请求和回复包等等。它可以很容易地处理一些典型操作&#xff0c;比如端口扫描&#xff0c;tracerouting&…

MoveAbsJ在使用时和MOVEJ有什么区别

问 题&#xff1a; MoveAbsJ在使用时和MOVEJ有什么区别 回 答&#xff1a; MoveAbsJ的目标点是用六个轴伺服电机的偏转角度值来指定的。 MOVEJ和MOVEL的目标点是用坐标系X Y Z的值来指定的。

Python中的序列操作

Python中的序列操作 分类: python undefined 官方手册&#xff1a;https://docs.python.org/3.7/library/stdtypes.html#sequence-types-list-tuple-range 序列简介 序列是指按照位置顺序来存储数据的数据结构&#xff0c;也就是说能通过数值索引进行操作。实际上&#x…

automaticallyAdjustsScrollViewInsets的作用

简单点说就是automaticallyAdjustsScrollViewInsets根据按所在界面的status bar&#xff0c;navigationbar&#xff0c;与tabbar的高度&#xff0c;自动调整scrollview的 inset,设置为no&#xff0c;不让viewController调整&#xff0c;我们自己修改布局即可~转载于:https://ww…

JavaScript 基础知识 - BOM篇

前言 本篇文章是JavaScript基础知识的BOM篇&#xff0c;如果前面的《JavaScript基础知识-DOM篇》看完了&#xff0c;现在就可以学习BOM了。 注意&#xff1a; 所有的案例都在这里链接: 提取密码密码: yvxo&#xff0c;文章中的每个案例后面都有对应的序号。 1. BOM 基本概念 B…

全球首例机器人自杀事件 因受够无聊家务

据凤凰网,一个奥地利家庭购买一小机器人,每天工作就是倒垃圾、倒垃圾。一天完工后,它竟自己启动,爬到炉边&#xff0c;推开上面的锅&#xff0c;把自己活活烧死…专家称这个机器人实在受够了无聊的家务琐事&#xff0c;才毅然选择自杀机器人也是有尊严的!为这有骨气的robot点根…

【python基础】——数据类型(列表、字典、集合)

骏马金龙——python语法基础 python基础 变量与运算 符号//%**意义整除整除取余幂次方数据种类 #mermaid-svg-7nSRRijcYFCYwTDr .label{font-family:trebuchet ms, verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-7nSRRijcYFCYw…

linux命令:mkdir命令

命令参数&#xff1a; -m, --mode模式&#xff0c;设定权限<模式> (类似 chmod)&#xff0c;而不是 rwxrwxrwx 减 umask -p, --parents 可以是一个路径名称。此时若路径中的某些目录尚不存在,加上此选项后,系统将自动建立好那些尚不存在的目录,即一次可以建立多个目录; …