zoukankan      html  css  js  c++  java
  • 《程序设计与数据结构》第七周学习总结

    学号 20172326 《程序设计与数据结构》第七周学习总结

    教材学习内容总结

    AVL树

    • AVL树是实现平衡二叉树的一种算法实现,别的方法也可实现例如红黑树。

    • 平衡因子:右子树高度-左子树高度的差值(高度是指当前结点到叶子结点的最长路径,如所有叶子结点的高度都为0,而深度则是指从根结点到当前结点的最大路径,如根结点的深度为0。),规定AVL树的平衡因子不大于1。所以在一个已经实现的AVL树中,任意一个节点的平衡因子的取值为(1,-1,0)。

    • 左旋,右旋,左右旋,右左旋。当进行插入抑或是删除操作时,可能导致一个平衡二叉树失衡,因此就要对其进行调整,使其恢复平衡。可以简单的用列表遍历这个数,重新构造一个新的树,但这种方法太过简单,粗暴。所以我们可以使用旋转树,来使其恢复平衡。以下是几种失衡的AVL树。

    • 右旋(或称为左左旋),经过计算,发现某一失衡点的左子树的深度大于右子树,这时就需要将左子树向“右”旋转,使其恢复平衡。方法:将失衡点的左结点设为当前树的新根,使原根变为新根的右结点,将新根的右结点变为原根的左结点。

    • 左旋,失衡点处右子树的深度大于左子树,将右子树向左旋转。原理与右旋对称。

    • 当某一子树太深时,仅通过一次右旋或左旋将无法完成,因此,我们需要使用两次旋转来实现它。因为旋转方法总是对称的,所以,只拿左右旋转为例。

    • 左右旋,当左孩子的右子树太深时,进行旋转。先将其父节点变为其左孩子,同时将其左左孩子进行分配。

    • 在执行结束操作后,通过比较每个结点的平衡因子来维持AVL树的平衡

    红黑树

    • 根结点为黑色;每个结点只有红色或黑色;所有的叶结点为黑色;红结点的孩子均为黑色;对于每个结点,从该结点到其叶子结点构成的所有路径上的黑结点个数相同;

    • 插入结点。1.插入结点为对应的根结点,直接将其涂黑即可。2.插入结点的父节点为黑结点,那么,直接插入即可。3.插入的父节点为红色。此时将破坏红黑树的结构,因此需要将其进行旋转。 分为以下三种情况,满足之一的条件时,进行递归。

      • 当插入结点的父结点为红,且叔结点也为红时,将其父结点与叔结点改为红色,祖父结点变为黑色。并将带操作结点改为祖父结点
      • 当待操作结点的父节点是红色的,叔叔节点是黑色的,且插入节点是其父节点的右子节点。这时,将待操作结点改为其父结点,再将带操作结点左旋。
      • 待操作结点的父结点是红色,叔叔结点是黑色,且插入结点是其父结点的左子结点。我们要做的操作有:将当前结点的父结点涂黑,将祖父结点涂红,在祖父结点为支点做右旋操作。最后把根结点涂黑,整个红-黑树重新恢复了平衡。
    • 删除操作:

      • 第一步:将红黑树当作一颗二叉查找树,将节点删除。
        这和"删除常规二叉查找树中删除节点的方法是一样的"。分3种情况:
        ① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
        ② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
        ③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。 在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个儿子,则按"情况② "进行处理。
      • 第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
        因为"第一步"中删除节点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。

    教材学习中的问题和解决过程

    • 问题1:
    Comparable<T> comparableElement = (Comparable<T>)element  
    

    为什么要使用comparable来将element转为相关格式呢?

    • 问题1理解:这是添加元素方法(addElement)的一部分代码,将传入的参数转化为comparable型。为什么不在传入的时候直接将其变为comparable类型呢?况且直接变为comparable型数据,在之后的维护平衡树时有助于进行插入元素之间的比较。我们知道,在方法中传入一个comparable参数看似简单,但在真正通过输入数据的时候,可能是一个string值,也可能是一个int值,但是,想传入一个comparable类型数据,基本不可能,所以,选择了在方法中进行类型转化。

    • 问题2:红黑树的平衡问题

    • 问题2理解:首先,我们知道,红黑树通过颜色来控制平衡。尤其是黑色结点,通过每条路径有相同数目的黑色结点。同时,每个红色结点的孩子结点为黑色。可以想象,想沿着一边子树的一个方向插入众多的结点将不可实现,从而实现了平衡。同时,根据二叉查找树的性质可知,若删除一个结点,除非该结点为根结点且为红色,否则必需经过一些操作以维持平衡。

    • 在课堂上,大家发现了一个问题。这种时候,应该怎样操作,对于结点(7)结点(8),无论将其变为黑色还是红色,都符合要求。那么,为什么还要将其涂为红色呢?

    • 涂为红色,有什么变化呢?相较于涂为黑色,黑色结点减少,且其孩子结点为黑色。如果是黑色呢?该树整体黑色结点增多。但问题恰恰就在这里,如果这是一颗单独的树还好说,但如果是某一棵树的子树呢?只有这一部分的黑色结点增加,别的均不变,这直接导致违反了规定。所以,从中也可以看出,对于红黑树,黑色结点的变化是相当慎重的。从规定插入的结点默认为红色也可以看出这点。

    代码问题

    • 问题1:AVL树实现动态平衡的方法
    • 理解:当顺序输入一组数据“1 2 3 4 5 6 7 8 9”时,如果没有相应的平衡方法,就会出现这种情况。
    • 这种情况之前博客也曾提到,是平衡二叉树决不能出现的情况,那么,应该如何处理呢?从思路分析,我们必须要更换根结点的元素,也就是说当一组数据顺序进入时,一旦违反平衡因子的大小,立刻对树中的元素进行对比,将能够平衡树的根找出来,类似于将之前的树折成两半。理想的效果是这样的,也就是说,平衡是动态的,随时根据平衡因子的大小来调整树的元素分布。

    因此,一开始,我的思路是,通过比较所有元素来确定一个中间大小的数据来作为根。可是,这一实现过程较为麻烦,需要将当前树进行重构。翻了翻书,看到大篇幅的旋转,我就知道应该怎样做了。可以看到,每次旋转,都是由部分到整体,从某一棵失衡的子树开始,再向上旋转,再判断再旋转。所以,这是一个重复的过程,因此,可以用递归实现。代码将较为简单。

              
    if (data.compareTo(p.getElement()) < 0) {//向左子树寻找插入位置
                p.setLeft(insert(data, p.getLeft())); 
     if (height(p.getLeft()) - height(p.getRight()) == 2) {//插入后计算子树的高度,等于2则需要重新恢复平衡,由于是左边插入,左子树的高度肯定大于等于右子树的高度
    
                    //判断data是插入点的左孩子还是右孩子
                    if (data.compareTo(p.getLeft().getElement()) < 0) {
                        //进行LL旋转
                        p = singleRotateLeft(p);
                    } else {
                        //进行左右旋转
                        p = doubleRotateWithLeft(p);
                    }
                }
            } 
    
    

    这里是部分代码,全部的有点长,就不进行展示。这里最为关键的就是这行代码“ p.setLeft(insert(data, p.getLeft()));
    ”也就是重复利用递归的部分,通过代替循环体,我们直接锁定需要插入的位置,在插入结束后,直接就插入结点的父结点的平衡因子来判断插入是否导致失衡,也就是与2进行判断,如果违规,直接进行旋转。这样一来,在树的底层位置进行旋转,尽可能缩小涉及到的范围,也就使得更加便于实现。

    代码托管

    其他(感悟、思考等,可选)

    • 本章的知识不好理解,尤其是红黑树,涉及了大量的不同情况,令人头秃,而且网上的知识有漏洞的也很多,继续自己体会。

    学习进度条

    代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 5000行 30篇 400小时
    第一周 0/0 1/1 3/3
    第二周 409/409 1/2 5/8
    第三周 1174/1583 1/3 10/18
    第四周 1843/3426 2/5 10/28
    第五周 539/3965 2/7 20/48
    第六周 965/4936 1/8 20/68
    第七周 766/5702 1/9 20/88

    结对及互评

    • 博客中值得学习的或问题:
      排版精美,对于问题研究得很细致,解答也很周全。
    • 代码中值得学习的或问题:
      代码写的很规范,思路很清晰,继续加油!

    点评过的同学博客和代码

    结对学习内容

    • 第十一章 二叉查找树

    参考资料

  • 相关阅读:
    NPOI使用手册
    MySQL索引-B+树
    IDEA Pycharm WebStorm JetBranis全版本 2020年 最新激活方式
    SpringBoot整合MyBatis
    js-cookie的用法
    Vue项目devServer.proxy代理配置详解
    子级div设置margin属性影响父级位置
    深拷贝和浅拷贝的区别和与原理
    css如何将div画成三角形
    macos10.15.4以上svn报错svn: error: The subversion command line tools are no longer provided by Xcode解决
  • 原文地址:https://www.cnblogs.com/326477465-a/p/9899047.html
Copyright © 2011-2022 走看看