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

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

教材学习内容总结

  • 二叉查找树是一种含有附加属性的二叉树,其左孩子小于父结点,父结点小于或者等于右孩子。

用链表实现二叉查找树

  • addElement操作:根据给定元素的值,在树中的恰当位置添加该元素。
    • 判断元素是不是Comparable,不是则抛出异常。
    • 树为空:新元素成为根结点。
    • 树非空:新元素与根元素进行比较
      • 小于:如果根的左孩子为空,成为根的左孩子;左孩子不空,遍历添加。
      • 大于:如果根的右孩子为空,成为根的右孩子;右孩子不空,遍历添加。
  • removeElement操作:从二叉查找树中删除给定的Comparable元素;找不到则抛出异常。
    • 选择替换结点的三种情况:
      (1)被删除结点没有孩子,replacement返回null;
      (2)被删除结点有一个孩子,replacement返回这个孩子 ;
      (3)被删除结点有两个孩子,replacement返回中序后继者;(处于根结点右子树上)
  • removeAllOccurrences操作:从二叉查找树中删除指定元素的所有存在。
    • 方法使用了LinkedBinaryTree类的contains方法。
  • removeMin操作:
    • 最小元素在二叉查找树的可能情况:
      (1)树根没有左孩子,树根即为最小元素,树根右孩子变成新的根结点;
      (2)树的最左侧结点为一片叶子,该叶子即为最小元素,设置其父结点的左孩子应用为null;
      (3)树的最左侧结点为内部结点,设置其父结点的左孩子引用指向最小元素的右孩子。

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

  • 问题1:关于书P228页的中序后继者的理解。
  • 问题1解决方案:
    • 所谓的中序后继者意思是:中序遍历二叉树结点的后继结点
    • 如何查找中序后继者?
      • 若右子树不为空,则找到右子树最左的叶子节点;
      • 若右子树为空,且拥有右父亲节点,则找到右父亲节点;
      • 若右子树为空,且拥有左父亲节点,则找到最近的右祖先节点;
    • 而对于删除结点有两个孩子的情况时,不一定replacement返回中序后继者。也可以返回中继前驱者。 具体的需要看代码实现,而不需要局限于书本。
    • 如何查找中序前驱者?
      • 若左子树不为空,则找到左子树的最右的叶子节点;
      • 若左子树为空,且拥有左父亲节点,则找到左父亲节点;
      • 若左子树为空,且拥有右父亲节点,则找到最近的左父祖先节点;
  • 问题2:
  • 问题2解决方案:XXXXXX
  • ...

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

  • 问题1:是否需要定义新的指针类AVLTreeNode,换句话说,AVL树和二叉查找树以及链表实现的二叉树之间的关系。
  • 问题1解决方案:
    • 首先,根据书上P240所述

      由于需要上溯树,因此AVL树通常最好实现为每个结点都包含一个指向其父结点的引用。

    • 这里的上溯树是因为,树因为插入结点或者删除结点而变得不平衡,所以每次在进行这两个操作的时候,需要更新平衡因子,从插入或者删除的那个结点开始,检查到根结点。所以,我们的指针类很可能除了指向左右孩子的指针,还需要一个指向父结点的。
    • 其次,根据书上P239所述

      对于树中的每个结点,我们都会跟踪其左、右子树的高度。

    • 由此,指针类会需要一个int型变量height,来得出结点的高度。
    • 在我实现了指针类AVLTreeNodeLinkedAVLTree的平衡方法后,我需要实现添加和删除方法。但是,AVL树和二叉查找树唯一不同的是添加和删除中如果不平衡要进行旋转。 所以,AVL树是可以继承二叉查找树的。
    • 这时,其实我陷入了一个思维误区。我写的指针类AVLTreeNode因此肯定也要继承二叉树指针类BinaryTreeNode。但是,其实根本不用这么麻烦呀!
      直接在BinaryTreeNode构建新的构造方法不就可以了!
    public BinaryTreeNode(T obj, LinkedBinaryTree<T> left, LinkedBinaryTree<T> right,int height)`
    • 存在的问题:
      虽然准确理解了AVL树中旋转平衡的操作,但是并没有整体理解代码与代码之间的关系。花费大量的时间做了无用功,同时让自己陷入了错误的循环。
      如果,我直接发现AVL树是二叉查找树的子类,那我也不会构建新的指针类。
      所以,解决代码问题,首先需要宏观的观察,确定好整体的架构,这便是UML类图的重要性。不然,尽管你细节处理的再完美,方向错了,便是越走越远。

      先设计,考虑所有的情况,再去实现。

  • 问题2:链表旋转方法的顺序问题。
  • 问题2解决方案:这里以右旋为例。
    • 根据书P238 给出右旋的操作

      • 使树根的左孩子元素成为新的根元素。
      • 使原根元素成为这个新树根的右孩子元素。
      • 使原树根的左孩子的右孩子,成为原树根的新的左孩子。
    • 所以我们实现右旋方法就可以使用一下操作,其中node是原树根,node1是新树根。
    node1 = node.left;
    node1.right = node;
    node.left = node1.right;
    • 然后,添加上更新高度的操作。就可以返回新的根元素。
    node.height = Math.max(height(node.left),height(node.right));
    node1.height = Math.max(height(node1.left),height(node1.right));
    return node1;
    • 运行,首先给我抛出的是StackOverflowError错误。

    • 当应用程序递归太深而发生堆栈溢出时,抛出该错误。也就是说,方法里出现了死递归。这个问题,我在上周侯泽洋同学的博客中也看见过。
  • ...

代码托管

上周考试错题总结

上周无错题,优秀!

结对及互评

点评过的同学博客和代码

其他

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 0/0 1/1 10/10
第二周 610/610 1/2 20/30
第三周 593/1230 1/3 18/48
第四周 2011/3241 2/5 30/78
第五周 956/4197 1/6 22/100
第六周 2294/6491 2/8 20/120
第七周 914/7405 1/9 20/140

参考资料

猜你喜欢

转载自www.cnblogs.com/gk0625/p/9899030.html
今日推荐