20172301 《程序设计与数据结构》第七周学习总结
Posted gk0625
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
,来得出结点的高度。 - 在我实现了指针类
AVLTreeNode
和LinkedAVLTree
的平衡方法后,我需要实现添加和删除方法。但是,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 |
参考资料
以上是关于20172301 《程序设计与数据结构》第七周学习总结的主要内容,如果未能解决你的问题,请参考以下文章