20172302 《Java软件结构与数据结构》第七周学习总结


2018年学习总结博客总目录:[第一周](https://www.cnblogs.com/hzy0628/p/9606767.html) [第二周](https://www.cnblogs.com/hzy0628/p/9655903.html) [第三周](https://www.cnblogs.com/hzy0628/p/9700082.html) [第四周](https://www.cnblogs.com/hzy0628/p/9737321.html) [第五周](https://www.cnblogs.com/hzy0628/p/9786586.html) [第六周](https://www.cnblogs.com/hzy0628/p/9825081.html) [第七周](https://www.cnblogs.com/hzy0628/p/9873230.html)

教材学习内容总结

第11章 二叉查找树

1.二叉查找树是一种含有附加属性的二叉树,该属性即其左孩子小于父节点,而父节点又小于等于其右孩子。二叉查找树的一个示意图:

在二叉查找树中:
       (01) 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
       (02) 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
       (03) 任意节点的左、右子树也分别为二叉查找树。

2.二叉查找树的接口类继承自二叉树的接口类BinaryTreeADT,在其基础上补充了一些操作方法,代码如下:

public interface BinarySearchTreeADT<T> extends BinaryTreeADT<T>
{
    public void addElement(T element);
    //往树中添加一个元素
    public T removeElement(T targetElement);
    // 从树中删除一个元素
    public void removeAllOccurrences(T targetElement);
    // 从树中删除所指定元素的任何存在
    public T removeMin();
    //删除树中最小元素
    public T removeMax();
    // 删除树中最大元素
    public T findMin();
    //返回一个指向树中最小元素的引用
    public T findMax();
    //返回一个指向树中最大元素的引用
}

3.链表实现二叉查找树
       LinkedBinarySearchTree类它继承自LinkedBinaryTree类,是对LinkedBinaryTree类的一种拓展,同时它还实现了BinarySearchTreeADT接口类。

  • 构造函数
    • 创建空的LinkedBinarySearchTree
public LinkedBinarySearchTree()
{
        super();
}
- 根结点为特定元素的LinkedBinarySearchTree
public LinkedBinarySearchTree(T element)
    {
        super(element);

        if (!(element instanceof Comparable))
            throw new NonComparableElementException("LinkedBinarySearchTree");
    }
  • addElement操作
    1.树空,新元素成为树的根结点;
    2.非空,与树根元素进行比较;
    (1)小于根元素:①左孩子为null:新元素成为根的左孩子;②左孩子不为null:遍历根的左孩子,再次进行比较操作;
    (2)大于等于根元素:①右孩子为null:新元素成为根的右孩子;②右孩子不为null:遍历根的右孩子,再次进行比较操作;

  • removeElement操作
           找到被删除结点的替换结点,删除被删除结点,替换为替换结点。
           选择替换结点的三种情况:
    (1)被删除结点没有孩子,replacement返回null;
    (2)被删除结点有一个孩子,replacement返回这个孩子;
    (3)被删除结点有两个孩子,replacement返回中序后继者;(处于根结点右子树上)
    示意图:

  • removeAllOccurrences操作
    删除指定元素的所有存在,方法代码:

public void removeAllOccurrences(T targetElement)
            throws ElementNotFoundException
    {
        removeElement(targetElement);

        try
        {
            while (contains((T)targetElement))
                removeElement(targetElement);
        }

        catch (Exception ElementNotFoundException)
        {
        }
    }
  • removeMin操作
    最小元素在树中位置的3种情形:
    (1)树根没有左孩子,树根即为最小元素,树根右孩子变成新的根结点;
    (2)树的最左侧结点为一片叶子,该叶子即为最小元素,设置其父结点的左孩子应用为null;
    (3)树的最左侧结点为内部结点,设置其父结点的左孩子引用指向最小元素的右孩子。

4.用有序列表实现二叉查找树
LinkedBinarySearchTree类有很多方法与有序列表的方法之间存在着一一对应关系

  • 平衡二叉查找树的假设下,有序链表与的链表实现分析和二叉查找树的实现分析。

           add和remove操作有可能使得二叉查找树不再平衡,那么我们的二叉查找树的操作复杂度将会大大增加。

5.平衡二叉查找树
       如果二叉查找树不平衡,那么其效率可能比线性结构还要低。接下来讨论一些常用的平衡技术。
(1)右旋
使用条件:根结点左孩子的左子树过长导致不平衡
使用方法:①树根左孩子元素成为新的根元素②原树根元素称为新树根的右孩子元素③使原树根左孩子的右孩子,成为原树根的新的左孩子

(2)左旋
使用条件:根结点右孩子的右子树过长导致不平衡
使用方法:①树根右孩子元素成为新的根元素②原树根元素称为新树根的左孩子元素③使原树根右孩子的左孩子,成为原树根的新的右孩子

(3)右左旋
使用条件:根结点的右孩子的左子树过长导致不平衡
使用方法:①先让右孩子的左孩子,绕树根右孩子进行一次右旋②树根的新的右孩子绕树根左旋一次
(4)左右旋
使用条件:根结点的左孩子的右子树过长导致不平衡
使用方法:①先让左孩子的右孩子,绕树根左孩子进行一次左旋②树根的新的左孩子绕树根右旋一次

6.AVL树
       平衡因子:右子树的高度减去左子树的高度。

平衡因子<-1 平衡因子>1
孩子的平衡因子<=-1 右旋 右左旋
孩子的平衡因子>=1 左右旋 左旋

7.红黑树
红黑树的特性:
       (1)每个节点或者是黑色,或者是红色。
       (2)根节点是黑色。
       (3)每个叶子节点(Null)是黑色。 [注意:这里叶子节点,是指为空(NULL)的叶子节点!]即每个空结点为黑色,也即默认结点为黑色
       (4)如果一个节点是红色的,则它的子节点必须是黑色的。
       (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
红黑树示意图:

  • 红黑树中元素插入
  • 红黑树中元素删除
           关于红黑树的元素插入和删除,这两部分将在教材学习问题中进行总结。

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

  • 问题1:关于教材242页中所说的“据此我们可以论证具有n个节点红黑树的最大高度约为2*logn”,这句话不能理解,从哪里论证而来的结果?

  • 问题1解决方案:自己想试着推一下,推的话我想的是从其满足红黑树的特征下手,但试了一会没有结果,于是就从网上查找资料,给出的一个办法是用归纳总结法去证明这一结论。
    "一棵含有n个节点的红黑树的最大高度约为2*logn"等价于“高度为h的红黑树,它的包含的节点个数至少为2^(h/2)个”。
    从某个节点x出发(不包括该节点)到达一个叶节点的任意一条路径上,黑色节点的个数称为该节点的黑高度(x's black height),记为bh(x)。关于bh(x)有两点需要说明:
           第1点:根据红黑树的特性——从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点可知,从节点x出发到达的所有的叶节点具有相同数目的黑节点。这也就意味着,bh(x)的值是唯一的!
           第2点:根据红黑色的特性——如果一个节点是红色的,则它的子节点必须是黑色的可知,从节点x出发达到叶节点"所经历的黑节点数目">= "所经历的红节点的数目"。假设x是根节点,则可以得出结论"bh(x) >= h/2"。进而,我们只需证明 "高度为h的红黑树,它的包含的内节点个数至少为 2^bh(x)个"即可。
    到这里,我们将需要证明的定理已经由"一棵含有n个节点的红黑树的高度至多为2log(n)" 转变成只需要证明"高度为h的红黑树,它的包含的内节点个数至少为 2bh(x)个"。

下面通过"数学归纳法"开始论证高度为h的红黑树,它的包含的节点个数至少为 2^bh(x)个"。
(01) 当树的高度h=0时,内节点个数是1,bh(x) 为1,2^bh(x)也为 1。显然,原命题成立。
(02) 当h>0,且树的高度为 h-1 时,它包含的节点个数至少为 2^(bh(x)-1)。这个是根据(01)推断出来的!
       下面,由树的高度为 h-1 的已知条件推出“树的高度为 h 时,它所包含的节点树为 2bh(x)”。
       当树的高度为 h 时,
       对于节点x(x为根节点),其黑高度为bh(x)。
       对于节点x的左右子树,它们黑高度为 bh(x) 或者 bh(x)+1。
       根据(02)的已知条件,我们已知 "x的左右子树,即高度为 h-1 的节点,它包含的节点至少为 2^(bh(x)-1) 个";
       所以,节点x所包含的节点至少为 ( 2^(bh(x)-1) ) + ( 2^(bh(x)-1) ) + 1 = 2^bh(x)。即节点x所包含的节点至少为 2bh(x)。
       因此,原命题成立。
       由(01)、(02)得出,"高度为h的红黑树,它的包含的内节点个数至少为 2^bh(x)个"。
       因此,“一棵含有n个节点的红黑树的高度至多为2log(n)”。

  • 问题2:关于红黑树的元素插入、删除

  • 问题2解决方案:

插入

首先,我们插入的结点设置颜色为red,如果其父节点为black,那么没有违背红黑树任何条件,插入即可,但如果其父节点为red,那么需要调整,因为红色结点的孩子颜色不能为红色。

       插入一个结点时应该关注其叔叔结点?
因为在父结点为红色的情况下,我们调整时必定要改动其父结点所构成的树颜色,而这里发生改变,那么一定导致祖父结点处的左右子树不能再满足“从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点”,那么我们需要对叔叔结点也进行改动,才能保持红黑树特性。

下面来看需要调整的三种情况(父结点为红色,此时祖父结点必为黑色,以父结点为祖父左孩子为例):
(1)叔叔结点为红色

此时我们需要把叔叔结点d和父亲结点b都设为黑色,这样不改变祖父结点c下的“从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点”特性;
但还需将祖父c结点设为红色(因为在从根结点到祖父往下的路径上多了一个黑结点),并将当前结点设为祖父结点,然后往上迭代进行;
(2)叔叔结点为黑色,且插入结点为父结点右孩子

此时需将插入结点绕其父结点b向左旋转,这步不改变树的各个路径上的黑结点数目,但仍不满足红色结点的孩子必为黑色,此时转换为(3)中情况;
       为什么要进行这一步转化?
因为我们现在仅靠改变c的左子树上的颜色是不能保持红黑树的特性的。
(3)叔叔结点为黑色,且插入结点为父结点左孩子

现在,我们先绕祖父结点c进行一次右旋,因为祖父结点c为黑色,此时b结点的右子树上要多一个黑色结点,同时仍不满足红色结点孩子都为黑色;
我们这时需把b结点设为黑色,另再将其左右两个孩子结点a、c设为红色。

删除
删除操作比较复杂,自己理解还存在一些困难,不再总结。
参考这篇博客学习红黑树的删除操作

代码调试中的问题和解决过程

  • 问题1:课本代码BinarySearchTreeList类出现错误,错误类型为both methods have same erasure, yet neither overrides the other

  • 问题1解决方案:没见过这个错误,去网上查找资料,它的解释是泛型类型擦除与重载和覆盖问题,

先看一段代码

public class Father {
    void test(Object o){}
}
class Son<T> extends Father{
     void test(T o){}//编译错误!
}

这段代码会报一个编译错误,both methods have same erasure, yet neither overrides the other。
这个错误的意思是,两个方法在类型擦除后,具有相同的原生类型参数列表,但是也不能覆盖另一个方法。
泛型类型在编译后,会做类型擦除,只剩下原生类型。如参数列表中的T类型会编译成Object,但是会有一个Signature。
尽管两个test方法具有相同的字节码,但是类型参数信息用 一个新的签名(signature) 属性记录在类模式中。JVM 在装载类时记录这个签名信息,并在运行时通过反射使它可用。
这就导致了这个方法既不能作为覆盖父类test方法的方法,也不能作为test方法的重载。

       看过之后没感觉,不太懂它在说什么?又看了好几遍,才终于有些明白,我的那个错误也是将我的方法中参数类型改为Object类型,但那样还是会报错,因为它的类型擦除后,父子类两个重名方法并不能覆盖。知道这之后,又查看自己的代码,终于找到了是在我的OrderedListADT类中出了问题,即OrderedListADT类在继承ListADT类时没有写出泛型,改过之后解决问题。

代码托管

       上周代码行数为12499行,现在为13751行,本周共1252行。

上周考试错题总结

上周考试没有错题

结对及互评

  • 本周结对学习情况
    • 20172308

    • 博客中值得学习的或问题:本周博客内容非常详细,尤其是有关红黑树的介绍部分以及教材代码理解部分。

    • 结对学习内容:第十一章——二叉查找树。

其他(感悟、思考等)

感悟

  • 本周的红黑树学习起来比较困难,书上的很多东西只是说出结论,并不解释原因,这就增大了阅读的难度,同时有些东西是后面用到才会知道它前面所讲到得一些东西。红黑树的删除我还弄不太明白,实在有些复杂,希望接下来能弄懂这里。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 0/0 1/1 15/15
第二周 572/572 1/2 16/31
第三周 612/1184 1/3 13/44
第四周 1468/2652 2/5 13/57
第五周 1077/3729 1/6 14/71 初步理解各个排序算法
第六周 1087/4816 1/7 17/88 认识树结构
第七周 1252/6068 1/8 19/107 平衡二叉树、AVL树、红黑树

参考资料

posted @ 2018-10-31 19:49  ◕‿◕  阅读(860)  评论(0编辑  收藏  举报
© 2018 GitHub, Inc.