20172303 2018-2019-1《程序设计与数据结构》第6周学习总结

20172303 2018-2019-1《程序设计与数据结构》第6周学习总结

教材学习内容总结

在了解了查找和排序后,咱们又从新将目光放回数据结构上,本章咱们将学习一种非线性数据结构——树。html

1、树的概述

1.树的概念

  • 概念:树是一种由结点和边组成的,经过结点来存储元素,边来链接结点构成层次结构的非线性数据结构。
  • 相关术语:
    • :位于树的顶层的惟一结点,一棵树只有一个根结点。
    • 双亲和孩子:在树的某两层上下结构中,位于较高层的结点是位于较低层结点的双亲,位于较低层的结点是位于较高层结点的孩子。一个结点只有一个双亲,但一个结点能够有多个孩子。
    • 兄弟:有同一个双亲的多个结点相互为对方的兄弟。
    • 叶子:没有孩子的结点称之为叶子。叶子不必定所有位于最底层。
    • 内部结点:一颗树中既不是根结点也不是叶结点的称为内部结点。
    • 祖先和子孙:用一个例子来具体说明,根是全部结点的祖先,全部结点都是根的子孙。
    • 高度/深度:一颗树的层数。
    • 度/阶:一棵树中任一结点所具备的最大孩子数目。

2.树的分类

  • 按度来划分
    • n元树:树的每一个结点的孩子数不超过n个孩子。
    • 二叉树:每一个结点最多有两个结点的数。
  • 按树是否平衡来划分
    • 平衡:树的全部叶子之间相差层数不超过一层的树称为稳定的树。含有m个元素的平衡n元树具备的高度为lognm。
    • 彻底树:底层叶子都位于树的左边的平衡树称为彻底树。
    • 满树:在一颗n元树中,全部叶子都位于一层,且除叶子外的每一个结点都有n个孩子,则该树被称做满树。

2、树的遍历

  • 树的遍历均是从根结点开始的,而后遍历树内全部结点,共有四种方法。
    • 前序遍历:从根结点开始,而后从左孩子开始访问每一个结点的全部孩子。
    public void preorder(ArrayIterator<T> iter){
        iter.add(element);
    
        if(left != null)
            left.preorder(iter);
    
        if(right != null)
            right.preorder(iter);
    }
    • 中序遍历:先访问根结点的左孩子,而后是根结点,最后是根结点的右孩子。
    public void inorder(ArrayIterator<T> iter){
        if(left != null)
            left.inorder(iter);
    
        iter.add(element);
    
        if(right != null)
            right.inorder(iter);
    }
    • 后序遍历:从左到右遍历全部孩子,最后访问根结点。
    public void postorder(ArrayIterator<T> iter){
        if(left != null)
            left.postorder(iter);
    
        if(right != null)
            right.postorder(iter);
    
        iter.add(element);
    }
    • 层序遍历:从根结点开始,从左至右访问每一层的全部结点。
    public void levelorder(TreeNode root)
    {
        ArrayDeque<TreeNode> deque=new ArrayDeque<TreeNode>();
        deque.add(root);//根节点入队
        while(!deque.isEmpty()){
            TreeNode temp=deque.remove();
            System.out.print(temp.val+"--");
            if(temp.left!=null){
                deque.add(temp.left);
            }
            if(temp.right!=null){
                deque.add(temp.right);
            }
        }
    }
  • 经过下图来看四种遍历方法的不一样:
    • 前序遍历:A、B、D、E、C
    • 中序遍历:D、B、E、A、C
    • 后序遍历:D、E、B、C、A
    • 层序遍历:A、B、C、D、E

3、二叉树

1.二叉树相关

  • 二叉树的基本形态
    • (a)为空树
    • (b)为仅有一个结点的二叉树
    • (c)为仅有左子树而右子树为空的二叉树
    • (d)为仅有右子树而左子树为空的二叉树
    • (e)为既有左子树又有右子树的二叉树。
    • 这里应特别注意的是,二叉树的左子树和右子树是严格区分而且不能随意颠倒的,(c)与(d)就是两棵不一样的二叉树。
  • 二叉树的性质(假设二叉树的根结点位于第1层)
    • (1)二叉树的第i层最多有2^(i-1)个结点。
    • (2)深度为k的二叉树最多有2^k-1个结点。
    • (3)对任何一颗二叉树,若是其叶子个数为n0,度为2的结点数为n2,则有n0=n2+1。
    • (4)具备n个结点的彻底二叉树的高度为[log2n]+1

2.实现树的策略

  • 数组实现
    • 计算策略:存储在数组中位置为n的元素,元素的左子结点存储在(2n+1)的位置,右子结点存储在(2*(n+1))的位置。
    • 模拟连接策略:每个元素都是一个结点类,每个结点存储其孩子的数组索引,而不是孩子指针的对象引用变量。
  • 链表实现
    • 设置一个新的TreeNode类,每一结点都将包含一个指针,它指向将要存储在该结点的元素,以及该结点全部可能孩子的指针。

3.二叉树的操做

  • 二叉树ADT
  • 在二叉树的操做中,并无添加和删除元素的操做。由于依据二叉树的用途和结构的不一样,添加元素和删除元素的位置也会有所不一样。除此以外,尤为是删除操做,会对二叉树结构产生很大的影响,删除的结点为根、内部结点或叶子时其他剩余的元素该如何处理是个很大的问题。

4.二叉树的应用

  • 表达式树
    • 原理:表达式树是使用栈来实现的,它实现了将输入的后缀表达式输出成树的形式。每次将操做数压入栈并建立单结点树中,遇到操做符时弹出两个单结点树(元素为操做数)构成一颗二叉树,以后不断反复两种操做(遇到操做数和遇到操做符的两种操做)直至最后栈中只存储了一颗完整的表达式树。
    • 此篇博客中有一个图文并茂的很是直观的例子,要比书上的例子好懂得多。
  • 决策树
    • 原理:决策树的结点用于表示决策点,其子结点表示在该决策点的可选项,叶结点表示决策的结果。使用决策树进行决策的过程就是从根结点开始,测试待分类项中相应的特征属性,并按照其值选择输出分支,直到到达叶子。二叉树能够用于实现简单决策树(回答只有是和否)

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

  • 问题1:在上一篇博客的问题三中我曾经提问说为何有些方法要先定义一个私有方法,而后再定义一个公用方法来调用私有方法?
  • 问题1解决方案:当时个人回答是以为为了格式一致才设置两个方法,可是在第十章中我找到了正确答案:当某些方法是以递归式编写的时候,它们一般须要使用一个私有支持方法,由于第一个调用和随后每一个调用的签名或行为多是不一样的。
  • 问题2:决策树中提到的is-a关系是什么关系?
  • 问题2解决方法:is-a(英语:subsumption,包含架构)指的是类的父子继承关系,例如类D是另外一个类B的子类(类B是类D的父类)。换句话说,一般"D is-a B"(B把D包含在内,或是D被包含在B内)指的是,概念体D物是概念体B物的特殊化,而概念体B物是概念体D物的通常化。举例来讲,水果是苹果、橘子、芒果与其余水果的通常化。

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

  • 问题1:在测试背部疼痛诊断器时,老是抛出错误
  • 问题1解决方法:依据抛出的错误来看应该是由于数据类型不一样,可是不论我怎么修改toString都没法消除错误。后来发现是因为LinkedBinaryTree中的getLeft()getRight()方法出现了问题,由于按照书上代码所写的它本来一直返回的是本来设置的rightleft而不是实际应该返回的根结点的左孩子或右孩子。
public BinaryTreeNode<T> getLeft() 
 {
    return left;
 }

public BinaryTreeNode<T> getRight() 
 {
    return right;
 }
  • getLeft()getRight()做出修改便可实现。

  • 问题2:在完成PP10.3时,不论选择哪的结点进行判断输出的都是true
  • 问题2解决方法:我本来想的是在isLeaf的方法中输入所要判断的结点做为参数,可是后来发现这样引用的时候会特别丑。
System.out.println(node1.isLeaf(node1));
  • 第一次修改的时候我只是把参数删掉了,此时的代码以下:
public boolean isLeaf(){
        if (left == null && right == null){
            return true;
        }
        else {
            return false;
        }
    }
  • 结果输出结果依然都为true,缘由是leftright都是开始时设定的结点其内容都为空,想要引用实际想要判断的结点的话要加上this
public boolean isLeaf(){
        if (this.left == null && this.right == null){
            return true;
        }
        else {
            return false;
        }
    }
  • 以后就能够了:

代码托管

上周考试错题总结(正确为绿色,错误为红色)

  • 本周没有测试。

结对及互评

点评模板:

  • 博客中值得学习的或问题:
    • 本周的博客相比上周又增长了不少加粗和变红的标注,能够看出对所学内容有了比之前更加深入的思考,因此会在博客中突出标记本身在学习过程当中发现的重点。
  • 代码中值得学习的或问题:
    • 本周代码中规中矩,没有什么突出的优势或问题。

点评过的同窗博客和代码

  • 本周结对学习状况
    • 20172322
    • 结对学习内容
      • 共同讨论了课后的PP项目。

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

  • 相比于以前本章学习的最大感触就是代码不是那么看得很懂了,以前的几章代码都看得很细很透彻,每一步是干什么的基本上都清楚,可是本章不知道是因为代码数量过多仍是什么其余的缘由,时常看着看着就不知道在干什么了。可能这就是线性数据结构和非线性数据结构的差别吧。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 10/10 1/1 10/10
第二周 246/366 2/3 20/30
第三周 567/903 1/4 10/40
第四周 2346/3294 2/6 20/60
第五周 2346/3294 2/8 30/90
第六周 1343/4637 2/10 20/110
  • 计划学习时间:30小时
  • 实际学习时间:20小时
  • 改进状况:因为各类缘由致使本周的学习时间没有达到预期标准,因此感受本章学的不是很好,以后要尽快补起来。

参考资料

相关文章
相关标签/搜索