20172332 2017-2018-2 《程序设计与数据结构》第七周学习总结

20172332 2017-2018-2 《程序设计与数据结构》第七周学习总结

教材学习内容总结

第十一章 二叉查找树

  • 1.二叉查找树:一种带有附加属性的二叉树。即每个结点其左孩子都要小于其父结点,而父结点又小于或等于其右孩子。(左孩子<父结点<=右孩子)
  • 2.二叉查找树不仅具有二叉树的操作,还具有以下的特殊操作:

    1333382-20181029211424701-968137420.jpg

  • 3.用链表实现二叉查找树:每个BinaryTreeNode对象要维护一个指向结点所存储元素的引用,另外还要维护指向结点的每个孩子的引用。
  • 4.二叉查找树的构造函数代码(引用了父类的两个构造函数):
   public LinkedBinarySearchTree() {super();}public LinkedBinarySearchTree(T element) {super(element);if (!(element instanceof Comparable))throw new NonComparableElementException("LinkedBinarySearchTree");}
  • 5.addElement操作:
    ①如果这个元素不是Comparable,则该方法会抛出NoComparableElementException异常;
    ②如果树为空,则这个新元素成为根结点;
    ③如果树非空,这个新元素会与树根元素进行比较,
    • (1)如果小于根结点中存储的元素且根的左孩子为null,则这个元素成为根的左孩子。
    • (2)如果这个新元素小于根结点中存储的那个元素且根的左孩子不是null,则会遍历根的左孩子并再次进行比较操作。
    • (3)如果这个新元素大于或等于树根存储的那个元素且根的右孩子为null,则这个新元素会成为根的右孩子。
    • (4)如果这个新元素大于或等于树根处存储的那个元素且根的右孩子不是null,则会遍历根的右孩子,并再次进行比较操作。

      1333382-20181029214054465-507945622.gif

  • 代码实现:
    public void addElement(T element) {if (!(element instanceof Comparable))throw new NonComparableElementException("LinkedBinarySearchTree");Comparable<T> comparableElement = (Comparable<T>)element;if (isEmpty())root = new BinaryTreeNode<T>(element);else {if (comparableElement.compareTo(root.getElement()) < 0){if (root.getLeft() == null) this.getRootNode().setLeft(new BinaryTreeNode<T>(element));elseaddElement(element, root.getLeft());}else{if (root.getRight() == null) this.getRootNode().setRight(new BinaryTreeNode<T>(element));elseaddElement(element, root.getRight());}}modCount++;}private void addElement(T element, BinaryTreeNode<T> node) {Comparable<T> comparableElement = (Comparable<T>)element;if (comparableElement.compareTo(node.getElement()) < 0){if (node.getLeft() == null) node.setLeft(new BinaryTreeNode<T>(element));elseaddElement(element, node.getLeft());}else{if (node.getRight() == null) node.setRight(new BinaryTreeNode<T>(element));elseaddElement(element, node.getRight());}}
  • 6.removeElement操作:(必须推选出另一个结点来代替要被删除的那个结点)
    ①找不到给定目标元素时,抛出ElementNotFoundException异常。
    ②如果被删除结点没有孩子,则replacement返回null。
    ③如果被删除结点只有一个孩子,则replacement返回这个孩子。
    ④如果被删除结点有两个孩子,则replacement会返回中序后继者(因为相等元素会放到右边)
  • 代码实现:
    public T removeElement(T targetElement) throws ElementNotFoundException {T result = null;if (isEmpty())throw new ElementNotFoundException("LinkedBinarySearchTree");else{BinaryTreeNode<T> parent = null;if (((Comparable<T>)targetElement).equals(root.element)) {result =  root.element;BinaryTreeNode<T> temp = replacement(root);if (temp == null)root = null;else {root.element = temp.element;root.setRight(temp.right);root.setLeft(temp.left);}modCount--;}else {                parent = root;if (((Comparable)targetElement).compareTo(root.element) < 0)result = removeElement(targetElement, root.getLeft(), parent);elseresult = removeElement(targetElement, root.getRight(), parent);}}return result;}private T removeElement(T targetElement, BinaryTreeNode<T> node, BinaryTreeNode<T> parent) throws ElementNotFoundException {T result = null;if (node == null)throw new ElementNotFoundException("LinkedBinarySearchTree");else{if (((Comparable<T>)targetElement).equals(node.element)) {result =  node.element;BinaryTreeNode<T> temp = replacement(node);if (parent.right == node)parent.right = temp;else parent.left = temp;modCount--;}else {                parent = node;if (((Comparable)targetElement).compareTo(node.element) < 0)result = removeElement(targetElement, node.getLeft(), parent);elseresult = removeElement(targetElement, node.getRight(), parent);}}return result;}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;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 = node.right;}result = current;}return result;}
  • 7.removeAllOccurrences操作:(使用了contains方法,find方法被重载以便利用二叉查找树的有序属性。)
    ①当在树中找不到指定元素时,则抛出ElementNotFoundException异常
    ②如果指定的元素不是Comparable,则该方法也会抛出ClassCastException异常。
    ③只要树中还含有目标元素,就会再次调用removeElement方法。
  • 代码实现:
public void removeAllOccurrences(T targetElement)throws ElementNotFoundException {removeElement(targetElement);try{while (contains((T)targetElement))removeElement(targetElement);}catch (Exception ElementNotFoundException){}}
  • 8.removeMin操作:
    ①如果树根没有左孩子,则树根就是最小元素,而树根的右孩子会变成新的根结点。
    ②如果树的最左侧结点是一片叶子,则这片叶子就是最小元素,这时只需设置其父结点的左孩子引用为null即可。
    ③如果树的最左侧结点是一个内部结点,则需要设置其父结点的左孩子引用指向这个将删除结点的右孩子。
  • 代码实现:
    public T removeMin() throws EmptyCollectionException {T result = null;if (isEmpty())throw new EmptyCollectionException("LinkedBinarySearchTree");else {if (root.left == null) {result = root.element;root = root.right;}else {BinaryTreeNode<T> parent = root;BinaryTreeNode<T> current = root.left;while (current.left != null) {parent = current;current = current.left;}result =  current.element;parent.left = current.right;}modCount--;}return result;}
  • 9.用有序列表实现二叉查找树:实现ListADT接口和OrderedListADT接口。
  • 10.BinarySearchTreeList实现的分析:add操作与remove操作都要求重新平衡化树。
  • 11.树实现中的有些操作更为有效,有些操作更为低效。
  • 12.蜕化树:无分支的树(效率比链表还低)
  • 13.平衡树的方法:
    • (1)右旋(左子树长):①使树根的左孩子元素成为新的根元素。②使原根元素成为这个新树根的右孩子元素。③使原树根的左孩子的右孩子,成为原树根的新的左孩子。
    • (2)左旋(右子树长):①使树根的右孩子元素成为新的根元素。②使原根元素成为这个新树根的左孩子元素。③使原树根右孩子结点的左孩子,成为原树根的新的右孩子。
    • (3)右左旋(树根右孩子的左子树长):①让树根右孩子的左孩子,绕着树根的右孩子进行一次右旋。②让所得的树根右孩子绕着树根进行一次左旋。
    • (4)左右旋(树根左孩子的右子树长):①让树根左孩子的右孩子绕着树根的左孩子进行一次左旋。②让所得的树根左孩子绕着树根进行一次右旋。
  • 14.实现二叉查找树:①AVL树。②红黑树。(自树根而下的路径最大长度必须不超过log2 n,而且自树根而下的路径最小长度必须不小于log2 n -1)
  • 15.平衡因子:右子树的高度减去左子树的高度
  • 16.使树变得不平衡有两种方法:①插入结点。②删除结点。
  • 17.AVL树的右旋:由下图可知我们是在结点T的左结点的左子树上做了插入元素的操作,我们称这种情况为左左情况,我们应该进行右旋转(只需旋转一次,故是单旋转)【步骤与右旋步骤一样】

    1333382-20181101184620887-1374530989.png

    • 过程:

      1333382-20181101185640538-1234274212.png

  • 18.AVL树的左旋:由下图可知我们是在结点T的右结点的右子树上做了插入元素的操作,我们称这种情况为右右情况,我们应该进行左旋转(只需旋转一次,故是单旋转)【步骤与左旋步骤一样】

    1333382-20181101192850668-1429958258.png

    • 过程:

      1333382-20181101192951548-1962237048.png

  • 19.AVL树的左右(先左后右)旋:如下图,只是单纯的进行一次旋转,得到的树仍然是不平衡的。所以应该进行二次旋转。

    1333382-20181101193559240-1551687031.png

  • 20.AVL树的右左(先右后左)旋:如下图,只是单纯的进行一次旋转,得到的树仍然是不平衡的。所以应该进行二次旋转。

    1333382-20181101193743278-1167265171.png

  • 21.红黑树:每个结点存出一种颜色(红色或黑色,通常用一个布尔值来实现,false等价于红色)
    • 红黑树的特性:
    • (1) 每个节点或者是黑色,或者是红色。
    • (2) 根节点是黑色。
    • (3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
    • (4) 如果一个节点是红色的,则它的子节点必须是黑色的。
    • (5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
  • 22.红黑树的元素添加及删除。

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

  • 问题1:replacement会返回中序后继者的中序后继者是什么东西?
  • 问题1解决方案:从被删除的结点出发经过它的右结点,然后右结点最左边的叶子结点就是中序后继结点。
        public Node getSuccessor(Node delNode){ //参数为被删除的节点 //定义一个当前节点的引用,直接让往下走一步,走到被删除节点的右节点 Node curr=delNode.right; Node successor=curr; //用来指向中级后续节点 Node sucParent=null; //用来指向中级后续节点的父节点 while(curr!=null){ sucParent=successor; successor=curr; curr=curr.left; } //循环停止,中级后续节点被找出 if(successor!=delNode.right){ //将后继节点的子节点(只可能有右节点)替补到后继节点的位置上 sucParent.left=successor.right; //将被删除的右孩子连接到后继节点的右节点上 successor.right=delNode.right; //将被删除的左孩子连接到后继节点的右节点上(就是用后继节点替换掉被删除的节点) } return successor; }
  • 问题引申:为什么要找中序后继者作为代替删除结点的位置。
  • 问题引申解决方案:如果直接删结点,整个树的大小顺序就乱了,所以需要考虑,在树中找到一个合适的节点来把这个结点给替换掉,用这种方法来保持整个树的稳定。需要在树中找出所有比被删除节点的值大的所有数,并在这些数中找出一个最小的数来。

  • 问题2:AVL树的作用。
  • 问题2解决方案:删除时树的平衡性受到破坏,提高它的操作的时间复杂度。而AVL树就不会出现这种情况,树的高度始终是O(lgN).高度越小,对树的一些基本操作的时间复杂度就会越小。

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

  • 问题1:红黑树的元素添加。
  • 问题1解决方案:
    • 步骤1:将红黑树当作一颗二叉查找树,将节点插入。
    • 步骤2:将插入的节点着色为"红色"。
    • 步骤3:通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。
      private void insert(RBTNode<T> node) {int cmp;RBTNode<T> y = null;RBTNode<T> x = this.mRoot;// 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。while (x != null) {y = x;cmp = node.key.compareTo(x.key);if (cmp < 0)x = x.left;elsex = x.right;}node.parent = y;if (y!=null) {cmp = node.key.compareTo(y.key);if (cmp < 0)y.left = node;elsey.right = node;} else {this.mRoot = node;}// 2. 设置节点的颜色为红色node.color = RED;// 3. 将它重新修正为一颗二叉查找树insertFixUp(node);}/* * 新建结点(key),并将其插入到红黑树中** 参数说明:*     key 插入结点的键值*/public void insert(T key) {RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null);// 如果新建结点失败,则返回。if (node != null)insert(node);}
  • 问题2:红黑树的元素删除。
  • 问题2解决方案:
    • 步骤1:将红黑树当作一颗二叉查找树,将节点删除。
    • 步骤2:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
      /* * 删除结点(node),并返回被删除的结点** 参数说明:*     node 删除的结点*/private void remove(RBTNode<T> node) {RBTNode<T> child, parent;boolean color;// 被删除节点的"左右孩子都不为空"的情况。if ( (node.left!=null) && (node.right!=null) ) {// 被删节点的后继节点。(称为"取代节点")// 用它来取代"被删节点"的位置,然后再将"被删节点"去掉。RBTNode<T> replace = node;// 获取后继节点replace = replace.right;while (replace.left != null)replace = replace.left;// "node节点"不是根节点(只有根节点不存在父节点)if (parentOf(node)!=null) {if (parentOf(node).left == node)parentOf(node).left = replace;elseparentOf(node).right = replace;} else {// "node节点"是根节点,更新根节点。this.mRoot = replace;}// child是"取代节点"的右孩子,也是需要"调整的节点"。// "取代节点"肯定不存在左孩子!因为它是一个后继节点。child = replace.right;parent = parentOf(replace);// 保存"取代节点"的颜色color = colorOf(replace);// "被删除节点"是"它的后继节点的父节点"if (parent == node) {parent = replace;} else {// child不为空if (child!=null)setParent(child, parent);parent.left = child;replace.right = node.right;setParent(node.right, replace);}replace.parent = node.parent;replace.color = node.color;replace.left = node.left;node.left.parent = replace;if (color == BLACK)removeFixUp(child, parent);node = null;return ;}if (node.left !=null) {child = node.left;} else {child = node.right;}parent = node.parent;// 保存"取代节点"的颜色color = node.color;if (child!=null)child.parent = parent;// "node节点"不是根节点if (parent!=null) {if (parent.left == node)parent.left = child;elseparent.right = child;} else {this.mRoot = child;}if (color == BLACK)removeFixUp(child, parent);node = null;}/* * 删除结点(z),并返回被删除的结点** 参数说明:*     tree 红黑树的根结点*     z 删除的结点*/public void remove(T key) {RBTNode<T> node; if ((node = search(mRoot, key)) != null)remove(node);    }
  • 问题3:按着书上讲的左旋右旋步骤来做,会出现错误

    1333382-20181102211553774-604786972.png
    1333382-20181102211734772-737276055.png

  • 问题3解决方案:
  • 问题代码的过程为下图,很显然出现了覆盖结点,导致丢失结点的问题。

    1333382-20181102213314547-48487630.gif

  • 改正:

    1333382-20181102211902808-1674137391.png
    1333382-20181102211956442-1617628756.png

  • 过程:

    1333382-20181102214301393-1858550176.gif

代码托管

1333382-20181102153044100-1627966362.png

上周考试错题总结

  • 无。

点评过的同学博客和代码

  • 本周结对学习情况
    • 20172326
    • 20172313
    • 结对学习内容
      • 教材第11章
  • 上周博客互评情况
    • 20172326
    • 20172313

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

  • 查找二叉树是二叉树的引申学习,难度真的很大!

学习进度条

代码行数(新增/累积)博客量(新增/累积)学习时间(新增/累积)重要成长
目标5000行30篇400小时
第一周0/01/12/2
第二周1010/10101/210/12
第三周651/16611/313/25
第四周2205/38661/415/40
第五周967/48332/622/62
第六周1680/65131/734/96
第七周2196/87091/835/131
  • 计划学习时间:30小时

  • 实际学习时间:35小时

  • 改进情况:AVL树和红黑树真的耗费了大量的时间!

参考资料

  • Java软件结构与数据结构(第4版)
  • 图解数据结构树之AVL树
  • 红黑树(五)之 Java的实现
  • 平衡二叉搜索树(AVL树)的原理及实现源代码(有图文详解和C++、Java实现代码)

转载于:https://www.cnblogs.com/yu757503836/p/9873494.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/570208.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA读取本地图片并展示

代码如下&#xff1a; //读取本地图片输入流 FileInputStream inStream new FileInputStream("D:/image/123.jpg");//byte数组用于存放图片字节数据 byte[] buff new byte[inSteam.available()];inStream.read(buff); inStream.close();//设置发送到客户端的响应内…

Java中map关于putAll()和“=”的区别

putAll()方法&#xff1a; 相同的key&#xff0c;map1.putAll(map2); map1中的key-value会被覆盖。 不同的key&#xff0c;map2中的key-value会被追加到map1中。“”的使用&#xff1a; map1 map2; map1中所有的key-value都会被map2中的key-value覆盖。

Neo4j简介

1、什么是Neo4j   Neo4j是由java实现的开源NOSQL图数据库   数据库分为关系型和非关系型两种类型。   其中非关系型又分为Graph(图形),Document(文档),Cloumn Family(列式)&#xff0c;以及Key-Value Store(KV),   这四种类型数据库分别使用不同的数据结构进行存储。因…

js比较日期大小

//获取当前时间 var currentTime new Date();//自定义时间 var customTime"2019-03-01 00:00:00"; customTime customTime.replace("-","/");//替换字符&#xff0c;变成标准格式 customTime new Date(Date.parse(customTime));if(currentT…

Docker-容器数据卷

docker容器数据卷相当于外置的移动硬盘 docker容器数据卷主要功能是&#xff1a;容器的持久化、容器间继承共享数据 特点&#xff1a; &#xff08;1&#xff09;数据卷可以容器之间共享或重用数据 &#xff08;2&#xff09;卷中更改可以直接生效 &#xff08;3&#xff09;数…

weblogic的输出打印日志设置

设置路径&#xff1a; 1、服务器——》控制&#xff0c;进入服务器设置 2、服务器启动——>设置参数 3、参数配置&#xff1a; -Xms2048m -Xmx4096m -XX:PermSize256m -XX:MaxPermSize512m -XX:UseConcMarkSweepGC -XX:CMSClassUnloadingEnabled -XX:DisableExplicitGC -…

支持向量机——Large Margin Classifier

支持向量机的目标是 \[\underbrace {\min }_\theta \left\{ {C\left[ {\sum\limits_{i 1}^m {{y^{\left( i \right)}}{\mathop{\rm Cos}\nolimits} {t_1}\left( {{\theta ^T}{x^{\left( i \right)}}} \right) \left( {1 - {y^{\left( i \right)}}} \right){\mathop{\rm Cos}\…

WebLogic清理缓存

清除缓存路径&#xff1a; D:\Oracle\Middleware\user_projects\domains\my_domain\servers\AdminServer\cache D:\Oracle\Middleware\user_projects\domains\my_domain\servers\AdminServer\tmp D:\Oracle\Middleware\user_projects\domains\my_domain\servers\AdminServer\…

洛咕11月月赛

当时只打了十几分钟&#xff0c;以为T1是结论题看了几分钟去看T2&#xff0c;写完贪心就不想做咕掉了。 T1 暴力就能A。如果用数组开大点&#xff0c;打表发现开到\(M*7\)即可。记得取模。当然可以滚掉数组。懒得给CODE。 T2 显然是贪心。只需从最低的跳到最高的&#xff0c;再…

怎么查询表中BLOB字段的大小

返回的是字节&#xff08;B&#xff09; select DBMS_LOB.GETLENGTH(name) from table;

2018-2019-1 20189210 《LInux内核原理与分析》第四周作业

第三章 这一章接触内核源代码&#xff0c;对内核源码进行编译和调试跟踪 一、预备知识&#xff1a; 内核&#xff1a;整个操作系统的最底层&#xff0c;它负责了整个硬件的驱动以及提供各种系统所需的内核功能。内核实质上是系统上面的一个文件而已&#xff0c;这个文件包含了驱…

IDEA 配置weblogic home时,weblogic version无法选择

问题原因&#xff1a; D:\Oracle\Middleware\wlserver_10.3缺少注册文件 解决方法&#xff1a; 把D:\Oracle\Middleware下的registry.xml文件&#xff0c;复制一份到D:\Oracle\Middleware\wlserver_10.3中就可以解决。

linux测试网络是否连通ping、telnet命令

ping 可以测试到目标机器的连通性&#xff0c;ping域名还可以得出解析IP。 ping ip地址 例&#xff1a;ping 127.0.0.1图例&#xff1a; telent测试端口是否开放。 telent ip地址 端口号 例如&#xff1a;telent 127.0.0.1 8080

2018ACM-ICPC国际大学生程序设计竞赛亚洲区域赛(青岛站)赛后总结

这是今年最后一次打铁&#xff0c;我已经打了一年的铁了。 还是想写一个总结&#xff0c;不然&#xff0c;什么都会没留下。 实际上在去青岛之前&#xff0c;我已经一个月都没有严格地训练自己了&#xff0c;从9月份CCPC秦皇岛站打铁之后&#xff0c;我就基本上开始怀疑自己了&…

引用js/css时,加入时间戳解决浏览器缓存问题

解决思路&#xff1a;在引入的url后面加上时间戳&#xff0c;每次请求获取最新的js/css var timestampnew Date().getTime()&#xff1b;<script type"text/javascript" src"****.js?t"timestamp></script>

MySQL数据库编译过程

编译安装MySQL 准备工作 卸载rpm方式安装的mysql-server、mysql 安装ncurses-devel和cmake包 yum -y install ncurses-devel cmake tar xf cmake-2.8.6.tar.gz -C /usr/src cd /usr/src/cmake-2.8.6/ ./configure && gmake && gmake install 这个过程需要时间源…

window.open()详解

语法 window.open(URL,name,specs,replace) var specs"height400,width600,alwaysRaised,directoriesno,toolbarno,titlebarno,menubarno,scrollbarsno, resizableno,locationno, statusno";返回值&#xff1a; oNewWindow : 对象(Element)。返回对新的 window 对…

深入了解Kubernetes REST API的工作方式

关于Kubernetes REST API的工作方式&#xff1a; 在哪里以及如何定义从REST路径到处理REST调用的函数的映射&#xff1f; 与etcd的交互发生在哪里&#xff1f; 从客户端发出请求到保存在etcd中对象的端到端路径是怎样的&#xff1f; Kubernetes REST框架 Kubernetes REST实现可…

mapper 判断条件为null

在双引号里面再判断不为空 <if test"name ! null and name !" > and name #{name} </if>

gitlab hook触发jenkins自动构建

意义&#xff0c;gitlab推送或合并代码后触发jenkin自动构建发布代码或者执行命令推送规则 1.安装gitlab和hook插件有时需要重启jenkins服务&#xff0c;可能兼容性问题&#xff0c;安装完之后构建中看不到hook插件 2.配置jenkins构建中的hook插件下图中圈出为必选&#xff0c;…