【零基础】学python数据结构与算法笔记12

news2024/12/25 0:09:41

文章目录

  • 前言
  • 74.AVL树的概念
  • 75.AVL:旋转
  • 76.AVL:旋转实现1
  • 77.AVL:旋转实现2
  • 78.AVL:插入
  • 79.AVL树应用与数据结构总结
  • 总结


前言

学习python数据结构与算法,学习常用的算法,
b站学习链接

74.AVL树的概念

首先看一下二叉搜索树的效率
平均情况下,二叉搜索树进行搜索的时间为O(logn)
最坏情况下,二叉树可能非常偏斜,这样搜索时间就会是O(n)
在这里插入图片描述
解决方法:

  • 随机化插入
  • AVL树
    随机化插入有个问题,就是可能不是同一时间插入,同一时间插入随机化的话,可以像快速排序法那样,但隔一会插一个,隔一会插一个,最终插入形成的树还是可能斜偏。
    AVL树可以较好解决这个问题,AVL是三个科学家提出的,AVL是三个的首字母。

AVL树:是一颗自平衡的二叉搜索树。
具有以下性质:

  • 根的左右子树的高度之差的绝对值不能超过1
  • 根的左右子树都是平衡二叉树
    一上面那个斜二叉树举例,50的左右子树没有,都是0,40的左子树没有,右子树是1,绝对值就是1,30的左子树没有,右子树是2,高度之差绝对值就是2,就不满足平衡。

下面这颗树是AVL树,上面的数字是左子树的高度-右子树的高度,都满足条件。
在这里插入图片描述
如果现在插入11,就是插到12的左子树,这样平衡就会打乱,需要树之间做一个平衡。这个平衡后面会讲。

75.AVL:旋转

插入一个节点可能会破坏AVL树的平衡,可以通过旋转来进行修正。
插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变。我们需要找出第一个破坏了平衡条件的节点,称之为K。K的两颗子树的高度差2。
不平衡的出现可能有4种情况

1.不平衡是由于对K的右孩子的右子树插入导致的:左旋
在这里插入图片描述
将10左旋到20的下方。
实际情况10,20,30带着好多子树 ,如下S3本来的是h高度,插入Key之后,高度加1,导致不平衡,
把P左旋到C的下方,连接S1,S2。
在这里插入图片描述
2.不平衡是由于对K的左孩子的左子树插入导致的:右旋

把30右旋到下面
在这里插入图片描述
和左旋类似,把P右旋到C的下面
在这里插入图片描述
3.不平衡是由于对K的右孩子的左子树插入导致的:右旋-左旋

先把20右旋到下面,再把10左旋到下面

在这里插入图片描述
对P的右孩子C的左子树G插入,S2和S3本来的h-1,在S2或者S3插入一个后发生不平衡。先将C右旋到G的下方,再将P左旋到G的下方。

在这里插入图片描述
4.不平衡是由于对K的左孩子的右子树插入导致的:左旋-右旋

和上一个做一个镜像差不多。

在这里插入图片描述
在这里插入图片描述

四种方法挺好记的
左左 右
右右 左
左右 左右
右左 右左

76.AVL:旋转实现1

根据上面的图写出左旋和右旋。平衡因子balance factor 这里选右子树-左子树,
在这里插入图片描述

这里注意
插入时旋转没有问题,可是右右或者左左删除导致的不平衡,bf就不是0了,
比方说s1,s2,s3本来都是h+1的高度,删除s1中的一个key,左旋,最后p.bf=0-1=-1,c.bf=(h+2)-(h+1)=1。

在这里插入图片描述

77.AVL:旋转实现2

对照之前的图写出右旋-左旋
在这里插入图片描述
镜像写出,左旋-右旋
在这里插入图片描述

78.AVL:插入

主要一个点是:从父亲节点往上找
直到更新到一个节点的bf(平衡因子)是0后就不再更新
如果是+1或-1则继续下一个更新,-2或2则判断此时类型,进行响应的旋转。
总的代码如下,类继承了之前的二叉树,在此之上写的AVL树和节点

class AVLNode(BiTreeNode):
    def __init__(self,data):
        BiTreeNode.__init__(self,data)
        self.bf = 0 #平衡因子,右子树为正,左子树为负
class AVLTree(BST):
    def __init__(self,li = None):
        BST.__init__(self,li)
    def rotate_left(self,p,c): #左旋
        s2 = c.lchild
        p.rchild = s2
        if s2:#判断是否有s2
            s2.parent = p
        c.lchild = p
        p.parent = c
        
        p.bf = 0
        c.bf = 0
        return c
    def rotate_right(self,p,c): #右旋
        s2 = c.rchild
        p.lchild = s2
        if s2:#判断是否有s2
            s2.parent = p
        c.rchild = p
        p.parent = c #把父节点连回来
        
        p.bf = 0
        c.bf = 0
        return c
    def rotate_right_left(self,p,c): #右旋-左旋
        g = c.lchild
        #右旋
        s3 = g.rchild
        c.lchild = s3
        if s3:#判断是否有s3
            s3.parent = c
        g.rchild = c #连回去
        c.parent = g
        #左旋
        s2 = g.lchild
        p.rchild = s2
        if s2:
            s2.parent = p
        g.lchild = p
        p.parent = g
        
        #更新bf
        if g.bf >0: # 插到s3 
            p.bf = -1
            c.bf = 0
        elif g.bf <0: #插到s2
            p.bf = 0
            c.bf = 1
        else: #插入的是g
            p.bf = 0
            c.bf = 0
        return g
    def rotate_left_right(self,p,c):#左旋-右旋
        g = c.rchild
        
        s2 = g.lchild
        c.rchild = s2
        if s2:
            s2.parent = c
        g.rchild = c
        c.parent = g
        
        s3 = g.lchild
        p.rchild = s3
        if s3:
            s3.parent = p
        g.rchild = p
        p.parent = g
        
        #更新bf
        if g.bf <0: # 即插到s2
            p.bf = 1
            c.bf = 0
        elif g.bf >0: #插到s3
            p.bf = 0
            c.bf = -1
        else: #插入的是g
            p.bf = 0
            c.bf = 0   
        return g
    def insert_no_rec(self,val):
        #1.和BST一样,先插入
        p = self.root
        if not p:#如果是空树
            self.root = AVLNode(val)
            return
        while True:
            if val < p.data:
                if p.lchild:  #如果左子树存在,则根节点为这颗左树的根
                    p =p.lchild 
                else:#左孩子不存在,就插到这个位置上
                    p.lchild = AVLNode(val)
                    p.lchild.parent = p
                    node = p.lchild  #node存储的是插入的节点
                    break
            elif val > p.data:
                if p.rchild:
                    p = p.rchild
                else:
                    p.rchild = AVLNode(val)
                    p.rchild.parent = p
                    node = p.rchild  ##
                    break
            else:   # val ==p.data值就等于这个值,就相当于 插第二遍这个值,不允许插return
                return 
        
        #2.更新balanc factor
        while node.parent: #node.parent 不空,一直循环到根节点
            if node.parent.lchild == node: #如果是左子树来的,左子树更沉了
                #更新node.parent的bf -= 1 #选择左边-1 #右边+1 的平衡方法
                if node.parent.bf <0: #原来的node.parent.bf == -1,现在要更新后变为-2
                    #做旋转 
                    #看node哪边沉
                    g = node.parent.parent  #为了连接旋转之后的子树
                    x = node.parent   #旋转前子树的根
                    if node.bf >0: #如果是右边沉 #左孩子的右子树
                        n = self.rotate_left_right(node.parent,node) #具体更新函数里有
                    else: #如果左边沉 左左 - 右
                        n = self.rotate_right(node.parent,node)
                        #记得:把n和g连起来
                elif node.parent.bf >0: #原来node.parent.bf = 1 ,现在要更新后变为0
                    node.parent.bf = 0
                    break
                else: #原来node.parent.bf = 0 更新之后变为-1
                    node.parent.bf = -1
                    node = node.parent #往父一级更新
                    continue
                
            else:# 传递是从右子树传来的,右子树更沉了
                #更新node.parent的bf += 1 
                if node.parent.bf >0: #原来的node.parent.bf == 1,现在要更新后变为2
                    #做旋转 
                    #看node哪边沉
                    g = node.parent.parent  #为了连接旋转之后的子树
                    x = node.parent   #旋转前子树的根
                    if node.bf <0: #如果是左边沉 #右孩子的左子树 -右左
                        n = self.rotate_right_left(node.parent,node) #具体更新函数里有
                    else: #如果右边边沉 右右 - 左  
                        n = self.rotate_left(node.parent,node)
                    #记得连起来
                elif node.parent.bf <0: #原来node.parent.bf = -1 ,现在要更新后变为0
                        node.parent.bf = 0
                        break
                else: #原来node.parent.bf = 0.更新之后变为1
                        node.parent.bf = 1
                        node = node.parent #往父一级更新
                        continue
            # 连接旋转后的子树
            n.parent = g
            if g: #g不是空  #旋转之后node.parent 已经不是原来的parent了所以用x保存一下
                if x == g.lchild: #判断是根左子树连还是右子树连
                    g.lchild = n
                else:
                    g.rchild = n
                break #只可能是旋转后进入这里
            else: #g是空 n本身是根节点
                self.root = n 
                break

插入后如下,中序遍历依旧是升序
在这里插入图片描述

79.AVL树应用与数据结构总结

二叉搜索树拓展应用
B树(B-Tree):B树是一颗自平衡的多路搜索树。常用于数据库的索引。
哈希表也可以用做数据库的索引。
还有一种在此之上的改进叫B+树(B+Tree)大同小异

这个是3叉的B树,中间存两个数据17,35。比17小的存左边,17-35存右边,比17大的存右边。分成了三个块,查找时更块。
在这里插入图片描述

总结

学习了AVL树,数据结构到此告一段落。

文章目录

  • 前言
  • 74.AVL树的概念
  • 75.AVL:旋转
  • 76.AVL:旋转实现1
  • 77.AVL:旋转实现2
  • 78.AVL:插入
  • 79.AVL树应用与数据结构总结
  • 总结


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

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

相关文章

networkx学习(三) 小世界网络

networkx学习(三) 小世界网络 1.小世界网络模型 K-近邻规则网络的生成与可视化

彻底分析Arduino库安装和开发板库安装路径和方式

参考&#xff1a;https://blog.csdn.net/weixin_43794311/article/details/128631564&#xff0c;https://blog.csdn.net/t01051/article/details/103766886 一个最简单的安装esp8266和esp32的方法 在网址&#xff1a;https://arduino.me/download&#xff0c;下载对应的开发…

dp(七)把数字转化为字符串 (力扣版+牛客版) 跳台阶问题+最小花费跳台阶

目录 l剑指 Offer 46. 把数字翻译成字符串力扣版本 把数字翻译成字符串_牛客题霸_牛客网牛客版 滚动数组优化 跳台阶【一】 &#xff08;大数取模&#xff09;一 八个零七 最小花费爬楼梯 l剑指 Offer 46. 把数字翻译成字符串力扣版本 给定一个数字&#xff0c;按照对应的格…

【微信小程序入门到精通】—小程序实战构建售货平台首页

目录前言一、步骤阐述二、新建项目并梳理结构三、配置导航栏四、tabBar 实现五、轮播图实现总结前言 对于目前形式&#xff0c;微信小程序是一个热门&#xff0c;那么我们该如何去学习并且掌握之后去做实际项目呢&#xff1f; 为此我特意开设此专栏&#xff0c;在我学习的同时也…

买车是个计算题,看上了比亚迪的宋DMI,选择困难了,选择55km的还是,110km的,理科生一起计算下。

1&#xff0c;背景 赶时髦&#xff0c;啥新鲜就购买啥&#xff0c;最火的车子当然是比亚迪宋dmi。 大家都买说明还不错&#xff0c;买车还要排队。等上一阵子了。 而且可以省下购置税。 就按照最热销的110 km 的版本 17/1.13*0.1 1.50 w 按照发票上“价税总计”金额计算的话…

计算两个字符串的相似度difflib.SequenceMatcher

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 计算两个字符串的相似度 difflib.SequenceMatcher 选择题 对于以下python代码表述错误的是? from difflib import SequenceMatcher myText1"我想学习Python做人工智能项目" myTex…

SpringCloud-Netflix学习笔记01——SpringCloud入门

一、SpringCloud是什么 Spring官网&#xff1a;https://spring.io/ SpringCloud, 基于SpringBoot提供了一套微服务解决方案&#xff0c;包括服务注册与发现&#xff0c;配置中心&#xff0c;全链路监控&#xff0c;服务网关&#xff0c;负载均衡&#xff0c;熔断器等组件&#…

mybatis plus基本使用初体验02

1.常用注解 1.1 TableName注解 MyBatis-Plus在确定操作的表时&#xff0c;由BaseMapper的泛型决定&#xff0c;即实体类型决定&#xff0c;且默认操作的表名和实体类型的类名一致。若实体类类型的类名和要操作的表的表名不一致&#xff0c;会出现什么问题&#xff1f; 将数据…

力扣sql基础篇(八)

力扣sql基础篇(八) 1 大满贯数量 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # Championship的数字代表的就是赢得比赛的球员的id,可以使用行转列(UNION all) #如果涉及到分组函数,建议还是不要写除了分组字段外的其他字段,因为不太符合标准sql …

正则化:五重境界理解减少过拟合的神器

本文来自公众号“AI大道理” 正则化作为减少过拟合的手段被大量的使用&#xff0c;那么为什么会出现过拟合呢&#xff1f;正则化又是什么&#xff1f;是怎么样发挥作用的呢&#xff1f; 1、过拟合是什么&#xff1f; 过拟合是指模型在训练集上取得很高的识别性能&#xff0c…

Ubuntu 22.04配置静态IP地址

1、查看ip地址网卡名称&#xff1a;ifconfig 2、编辑网卡配置文件&#xff1a;sudo vim /etc/netplan/01-network-manager-all.yaml 默认样式&#xff1a; network: version: 2 renderer: NetworkManager 3、修改为&#xff1a; network: ethernets: enp7s0: dhcp4: no dhcp6: …

【Kotlin】集合操作 ① ( List 创建与元素获取 | 安全获取集合元素 | getOrElse | getOrNull )

文章目录一、List 创建与元素获取二、安全获取集合元素1、getOrElse 函数2、getOrNull函数三、List 创建与元素获取代码示例一、List 创建与元素获取 Kotlin 中的集合分为两类 , 只读集合 和 可变集合 ; 调用 listOf 函数 , 可以 直接创建 List 集合 ; 通过 [] 下标可以 直接获…

Spring系列 容器

创建容器 方式一&#xff1a;类路径加载配置文件&#xff1a; ApplicationContext ctx new ClassPathXmlApplicationContext("applicationContext.xml"); 方式二&#xff1a;文件路径加载配置文件&#xff08;用绝对路径&#xff09;&#xff1a; ApplicationConte…

闲话统信UOS

这周统信UOS正式在官网发布了22.0镜像统信UOS家庭版官网-正版国产操作系统--统信软件 (uniontech.com) 支持双系统&#xff1a;不影响原有系统使用&#xff0c;更换系统不担心电脑资料丢失&#xff1b; 官方应用商店&#xff1a;无病毒、放心安全下载&#xff0c;工作学习无打…

[C/Linux练习]进度条小程序

前置知识点 \n\r \n 换行&#xff0c;但只是垂直向下&#xff0c;并不水平移动。 \r 回车&#xff0c;返回当前行的头部。 光标返回头部后再打印&#xff0c;会从头开始覆盖之前打印在该行的内容。 printf的\n默认解释成换行回车。 光标是与显示器匹配的&#xff0c;光标在哪…

基于PHP的宠物社会化小程序

摘要随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&…

Java集合面试题

集合容器概述 什么是集合 集合框架&#xff1a;用于存储数据的容器。 集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。 任何集合框架都包含三大块内容&#xff1a;对外的接口、接口的实现和对集合运算的算法。 接口&#xff1a;表示集合的抽象数据类型。接口…

AlexNet,VGG,NiN,GoogleNet,批量归一化

学着学着突然发现&#xff0c;数据分析numpy&#xff0c;matplotlib&#xff0c;pandas这一部分内容没学完&#xff0c;还有pytorch的一些代码没有学完&#xff0c;所以在看像AlexNet这一些代码实现的时候&#xff0c;很多都看不懂&#xff0c;也得慢慢的补上来了hhh。这一周花…

Ajax的学习笔记(包括原生的ajax,jquery,axios,fetch)

一、什么是ajax AJAX 异步 JavaScript 和 XML。 AJAX 是一种用于创建快速动态网页的技术。 通过在后台与服务器进行少量数据交换&#xff0c;AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下&#xff0c;对网页的某部分进行更新。 二、ajxa的创建使…

【尚硅谷】Java数据结构与算法笔记10 - 树结构的基础部分

文章目录一、二叉树1.1 为什么需要树结构1.1.1 数组存储方式的分析1.1.2 链式存储方式的分析1.1.3 树存储方式的分析1.2 树示意图1.3 二叉树的概念1.4 二叉树 - 遍历节点1.5 二叉树 - 查找指定节点1.6 二叉树 - 删除节点二、顺序存储二叉树2.1 顺序存储二叉树的概念2.2 顺序存储…