20172310 2017-2018《程序设计与数据结构》(下)第七周学习总结

20172310 2017-2018《程序设计与数据结构》(下)第七周学习总结

教材学习内容总结

本章学习的是二叉查找树


11.1 概述

  • 二叉查找树(binay scarch tree)是种带有附加属性的二叉树,即对树中的每一个结点,其左孩子都要小于其父结点,而父结点又小于或等于其右孩子。
    html

  • 二叉查找树的定义是上章中讨论的二叉树定义的扩展。所以,下面的操做是二叉树中已定义的那些操做的补充。二叉查找树和平衡二叉查找树的接口是同样的程序列表。java


11.2 用链表实现二叉查找树

  • BinaryTreeNode在二叉查找树中也用来表示树中的每一个节点,每一个BinaryTreeNode对象要维护一个指向结点所存储元素的引用,另外还要维护指向结点的每一个孩子的引用。

  • addElement 操做:addElement方法根据给定元素的值,在树中的恰当位置添加该元素。
    添加元素位置的几种状况
    • 若是这个元素不是Comparable,则addElement方法会抛出NoComparableElementException异常。
    • 若是树为空,则这个新元素就将成为根结点。
    • 若是树非空,且它小于根结点中存储的那个元素且根的左孩子为null, 则这个新元素就将成为根的左孩子。若是这个新元素小于根结点中存储的那个元素且根的左孩子不是null, 则会遍历根的左孩子,并再次进行比较操做。
    • 若是这个新元素大于或等于树根存储的那个元素且根的右孩子为null, 则这个新元素会成为根的右孩子。若是这个新元素大于成等于树根处存储的那个元素且根的右孩子不是null,则会遍历根的右孩子,并再次进行比较操做

  • 一旦定义了但愿构造的树的类型和树的使用方式,也就可以定义出该树的接口和各类实现。

  • removeElement操做
    • removeElemcnt方法必须选出另外一个结点来代替要被删除的那个结点。private方法replacement返回指向一个结点的引用,该结点将代替要删除的结点。
      选择替换结点的三种状况以下:
    • 若是被删除结点没有孩子,则replacement返回null.
    • 若是被删除结点只有一 个孩子, 则replacement返回这个孩子。
    • 若是被删除结点有两个孩子,则replacement 会返回中序后继者(相等元素会放到右边)。
      且这个后继者不可能有左孩子节点,由于若是有左孩子节点则它的左孩子节点会成为那个后继者,而不是它,所以后继者要么没有孩子节点,要么只有右孩子。

二叉查找树的最右侧结点会存放最大元素,而其最左侧结点会存放最小元素.node

  • removeMin操做
    按照二叉树的定义,最小元素在二叉查找树中只会存在于最左边的位置,最左的位置分为有3种可能情形:
    • 若是树根没有左孩子,则树根就是最小元素,而树根的右孩了会变成新的根结点。
    • 若是树的最左侧结点是片叶子, 则这片叶子就是最小元素, 这时只需设置其父结点的左孩子引用为mull便可。
    • 若是树的最左侧结点是个内部结点,则须要设置其父结点的左孩 子引用指向这个将删除结点的右孩子。
  • removeMax操做同理

11.3用有序列表实现二叉查找树

  • 树的主要使用之一就是为其余集合提供高效的实现。

  • BinarySearchTreeList实现的分析:BinarySearchTreeList 的实现是一种任何结点的最大深度为log2(n)(其中n为树中存储的元素数目)的平衡二叉查找树。因此,add 操做和remove操做都要求从新平衡化树。
  • 虽然树实现中的有些操做更为有效,好比removeLast、last 和contains:但在利用树实现时,也有一些操做会变得低效,好比removeFirst和first。

11.4平衡二叉查找树

  • 蜕化树:如同链表通常,只有单边的子树。实际效率比链表还低。
  • 若是二叉查找树不平衡,其效率可能比线性结构的还要低。git

  • 平衡二叉树:任何一个节点的左右子树深度差不超过1.经过这个限定,阻止了二叉树的左右子树深度差较大的状况,维持了二叉树的稳定。算法

  • 平衡化技术中的一些方法:
    • 右旋:节点插入在最小不平衡树的右子树的右子树上面。 

      数据结构

    • 左旋:节点插入在最小不平衡节点的左子树的左子树上。

      函数

    • 右左旋:节点插入在最小不平衡树的右子树的左子树上面。
      学习

    • 左右旋:节点插入在最小不平衡节点的左子树的右子树上面
      设计

    • 用法总结:从发生不平衡的结点起,沿刚才回溯的路径取直接下两层的结点,若是这三个结点在一条直线上,则采用单旋转进行平衡化,若是这三个结点位于一条折线上,则采用双旋转进行平衡化。如图:

      3d

这是一篇很好的参考资料:数据结构——平衡二叉树

  • 为了解决上述二叉排序树这种左右子树深度由于插入或删除结点而不均匀的状况引入了两种方式红黑树和AVL树。

  • AVL树:是一种平衡二叉树的变体,其中最重要的一个概念是平衡因子:右子树的高度减去作紫薯的高度称为该结点的平衡因子。其旋转方式同上。
  • 红黑树:
    一、根节点是黑色。
    二、若是一个节点是红色的,则它的子节点必须是黑色的。
    三、从树根到树叶的全部路径上包含相同数目的黑色结点。
    四、每一个节点或者是黑色,或者是红色。
    五、每一个空结点为黑色。

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

  • 问题1:对课本removeElement方法的代码理解有困难,主要是后面的删除元素的图给错了,因而理解了半天。

  • 问题1解决方案:其实最重要的是replacement代码段的理解,课本给出了三种状况的分类,“若是被删除结点有两个孩子,则replacement 会返回中序后继者”这种类型的时候中序后继者是什么意思呢?我结合着课本给出的删除结点的示意图来理解。

若是删除的节点是10,它有左右孩子,中序遍历是先查左孩子,再是该节点,而后是右孩子,中序遍历查找的顺序是7,10,13,15 。如今10是当前所指向的结点,因此从这个步骤开始,继续接下去的遍历,也就是看下一个查找的元素,而后返回它,也就是返回13 。

private BinaryTreeNode<T> replacement(BinaryTreeNode<T> node) 
    {
        BinaryTreeNode<T> result = null;
        // 若是被删除结点没有孩子
        if ((node.left == null) && (node.right == null))
            result = null;
        //被删除的结点只有一个孩子,则返回孩子
        else if ((node.left != null) && (node.right == null))
            result = node.left;
        
        else if ((node.left == null) && (node.right != null))
            result = node.right;
        //被删除结点有两个孩子,就要找的比左边孩子小,但比该结点大的孩子。
        else
        {
            BinaryTreeNode<T> current = node.right;//建立一个结点current,存放当前节点的右孩子
            BinaryTreeNode<T> parent = node;
            
            while (current.left != null)//中序遍历找到下一个结点
            {
                parent = current;
                current = current.left;
            }
            
            current.left = node.left;//将被删除结点的左孩子链到找到的这个结点的左边

            if (node.right != current)//若是这个找到的节点不是原要被删除的结点的右孩子
            {
                parent.left = current.right;//这时current.right为null,parent.left从被找到的结点变为null
                current.right = node.right;
            }
            
            result = current;
        }
        
        return result;
    }
  • 问题2:对课本中红黑树的操做彻底看不懂。
  • 问题2解决方案:看完了课本对于红黑树操做的讲解,我不得不说,这些字我都认识,可放在一块儿我怎么就理解不了呢?(〃>皿<)
    对课本上给出的合法的红黑树的例子,我也表示怀疑,因此就去百度上找了一些资料来看看。
  • 红黑树中元素的插入:
    1.首先插入结点。插入的结点定为红色( 由于将插入的节点着色为红色,不会违背"从树根到树叶的全部路径上包含相同数目的黑色结点。",少违背一条特性,就意味着咱们须要处理的状况越少)。
    2.插入结点后的红黑树还是一棵二叉查找树,可是插入后变得再也不平衡了。以后从新平衡化或者说从新着色的过程则是一种迭代的过程,因此插入节点后,咱们要作的就是将破坏红黑树规则的结点经过从新着色上移到别的结点。
    3.接下来是分状况讨论如何变色和旋转,使其从新平衡。
    (1)其父节点为black,这种状况下没有违背红黑树任何条件,直接插入便可(包含被插入的节点为根节点的状况);
    (2)插入的结点的父节点是red的状况下,又可根据叔叔结点的case划分为三种状况来处理;
    - case1:当叔叔结点也为红色时,第一,先将父节点和叔叔结点变为black;第二,将祖父结点变为red;最后运用递归继续改变动底层的结点。

    - case2:当叔叔结点是black时,若是当前结点是父节点的左孩子,则将父节点染为black,在把祖父结点染为red,最后以祖父节点为支点进行右旋。

    - case3:当叔叔结点是black时,而当前结点是父节点的右孩子,则以该插入的结点的父节点做为当前节点进行左旋,变为case2进行处理。

  • 红黑树中元素的删除
    我以前已经总结过,在一棵平衡二叉树要删除一个结点有三种状况。
    这三种状况在红黑树中同样,但咱们须要作的是将删除后的二叉树从新平衡和染色,如今,又可分为如下几种状况:
    1)删除的结点为叶子节点,而后判断该节点是否为黑色的,若为true,则在删除后会致使黑高不相等。因而须要对红黑树进行调整。
    2)删除的结点有一个孩子节点,此时直接使用其孩子节点代替z节点,而后判断删除的节点是否为黑,若为true,则对红黑树进行调整。
    3)删除的结点有左右孩子节点,这时首先找到该节点的后继节点,有两种状况:第一种状况是y是z的右孩子节点,第二种状况是y不是z的右孩子节点。

调整的方式:

  1. 当删除状况为第一二种且结点为black时,咱们能够将替代原结点的节点x再加一种再额外增长一种黑色。(网上的资料都是这样解释的,可是而额外增长的究竟是怎么回事我尚未研究透彻,若是弄懂了后续再修改╭(╯^╰)╮)

在第三种状况中,经过使用z的后继节点y替换z节点,而后使用y的右孩子x来填补y的位置。在此过程须要将节点y进行移动,因为移动以后的y节点保持原来z节点的颜色,而x节点在代替y节点以后可能会出现问题,固然只会在y节点是黑色的状况下才会出现问题,当y节点为红色时,移动时红黑树的性质不会被破坏,y节点为黑色时,必定会出现黑高的不相等,而且也可能会出现两个连续的红色节点。这时须要对其进行下一步调整。

红黑树的删除
这是关于删除的一份资料,跟着这里面看能够理解不少,但本身仍是有些内容理解不了,主要是删除比插入的状况更多并且更复杂,理解起来仍是颇有难度的。

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

  • 问题1:

本身编写AVL树时,按照对左右旋和右左旋的理解编写的代码是这样的,可是参考网上的资料并和结对伙伴讨论发现,你们的代码是

这是为何呢?

  • 问题1解决方案:我发现其实本身的理解并无错,左右旋确实是先左旋,再右旋,那么问题在哪?你们广泛都是根据网上的代码来写的,左、右旋是这样的


但实际上左旋中的内容实际上是右旋时进行的,而右旋时的内容才是左旋的,我使用了参考的左、右旋代码,因而接下去本身写左右旋和右左旋的代码时出现了错误。
其实对这几个操做的理解仍是正确的。

  • 问题2:

  • 问题2解决方案:我看到这里出错了,还觉得是旋转操做的书写又发生了错误,但是再从新解读了一遍本身的代码发现彻底没有逻辑上的错误,因而又花了挺久的时间,还在同窗的帮助下发现本身真的是粗心,由于本身是仿照以前的代码写的,因此最开始定义的是一个node,而后如今须要一个element,结果构造函数中居然没有写,因而发生了这种错误。

代码托管

(statistics.sh脚本的运行结果截图)

上周考试错题总结

  • 上周无错题(ノ ̄▽ ̄)

结对及互评

点评:

  • 本周结对学习状况
  • 博客和代码中值得学习的或问题:

    • 看问题的角度很新颖,就像是咱们你们都在解决如何理解红黑树的插入和删除操做时,个人队友的想法倒是AVL树和红黑树能够达到相同的目的,那为何要创造出红黑树呢,进而去了解这二者的优缺点,又让我涨了知识,点赞。
    • 代码问题很细致,解答也挺详细的。

点评过的同窗博客和代码

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

这周的学习是创建在上周所学知识的基础上的,果真基础要打好,后面的学习才会更有效率。不得不批评一下淘宝买来的课本了,太坑爹了,图错了好几个,我一直研究,一直以为不对,后来事实证实我是正确的,不过仍是浪费了一些时间在这个上面。双周的课程比单周要少一些,因此这周自学Java的时间要多一些,果真知识都是靠时间换来的。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积)
目标 5000行 30篇 400小时
第一周 0/0 1/1 10/10
第二周 326/326 1/2 18/28
第三周 784/1110 1/3 25/53
第四周 2529/3638 2/5 37/90
第五周 1254/4892 2/7 20/110
第六周 1403/6295 2/9 32/142
第七周 1361/7656 1/10 35/177
  • 计划学习时间:30小时

  • 实际学习时间:35小时

  • 改进状况:这周在概念的理解上比以往花的时间要多。

参考资料

相关文章
相关标签/搜索