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

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

教材学习内容总结

本周学习了第10章php

  • 10.1概述
    • 树是一种非线性结构,有一个包含结点和边的集构成。元素保存在结点中,边则将结点链接起来。
    • 一些术语html

      根结点是位于树顶层的惟一结点
      位于树中较低层的结点是上一层结点的孩子,一个结点只有一个双亲
      同一双亲的两个结点称为兄弟
      根结点是树中惟一没有双亲的结点,没有任何孩子的结点称为叶子,其他为内部结点
      结点的层是从根结点到该结点的路径长度
      树的高度是指从根到叶子之间最远路径的长度java

    • 树中任一结点能够具备的最大孩子数目称为该树的度。若是度没有限制,就称为广义树,每一结点孩子数不超过n个的树称为n元树。度为2的树称为二叉树
    • 若是某树是平衡的(树的全部叶子都位于同一层或至少是彼此相差不超过一个层)且底层全部叶子都位于树的左边,则认为该树是彻底树。
    • 若是一颗n元树的全部叶子都位于同一层且每一结点要么是一片叶子要么正好有n个孩子,则称此树是满的node

  • 10.2实现树的策略
    • 计算策略:将元素n的左孩子置于位置(2xn - 1),将右孩子置于位置(2x(n + 1))。缺点是浪费大量存储空间。
    • 模拟连接策略:数组的每一元素都是一个结点类,每一结点存储的是每一孩子的数组索引。该策略容许连续分配数组位置而不用考虑树的彻底性。
    • 通常而言,一棵含有m个元素的平衡n元树具备的高度为lognm
  • 10.3树的遍历
    • 前序遍历:从根结点开始,访问每一结点及其孩子
    • 中序遍历:根结点-->左边孩子-->结点-->剩余结点
    • 后序遍历:根结点-->孩子-->结点
    • 层序遍历:从根结点开始,访问每一层的全部结点,一次一层
  • 10.4二叉树
操做 说明
getRoot 返回指向二叉树根的引用
isEmpty 断定该树是否为空
size 断定树中的元素数目
contains 断定指定目标是否在该树中
find 若是找到指定元素,返回指向其的引用
toString 返回树的字符串表示
iteratorInOrder 为树的中序遍历返回一个迭代器
iteratorPreOrder 为树的前序遍历返回一个迭代器
iteratorPostOrder 为树的后序遍历返回一个迭代器
iteratorLevelOrder 为树的层序遍历返回一个迭代器

在全部列举的操做中 ,不存在往树中添加元素的操做。在一些树中也没有删除树元素的操做git

  • 10.5使用二叉树:表达式树
    • 对表达式树的求值是从下往上的
  • 10.6背部疼痛诊断器
    • 决策树:其结点表示决策点,其子结点表示该决策点的候选项
    • 比较形象的一个决策树(有点误差在于这一节里讲的都是二叉决策树)
  • 10.7用链表实现二叉树
    • 方法以递归式编写的时候,它们一般须要一个私有支持方法,由于第一个调用和随后每一个用的签名和行为多是不相同的

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

  • 问题1:程序列表10.3中判断是不是运算符的方法
public boolean isOperator(){
    return (termType == 1);
}

为何须要判断termType为一时就是运算符web

  • 问题1解决方案:参考前面的ExpressionOp的定义,设置了两个int变量和一个char变量
private int termType;
private char operator;
private int value;

结点存储只须要一个int变量存储操做数,一个char变量存储运算符。多出来的int变量termType说明就不是用于存储操做数。在这个类中,我发现termType的用处都是用来判断是否为操做数的,因此设置这个变量的意义就应该在于事先在设置结点时,就标明这个结点存储的是什么东西,若是是运算符就把termType改成1,不是就设为其余数,这样就避免了在判断时要写一长串的判断条件if (operator == "+" || operator == "-" || operator == "*" || operator == "/")的状况express


  • 问题2:程序列表10.2ExpressionTree类提供了一个输出树的方法,不容易理解且输出是有问题的。
  • 问题2解决方案:结合具体代码进行分析
public String printTree() {
        UnorderedListADT<BinaryTreeNode<ExpressionTreeOp>> nodes = new ArrayUnorderedList<BinaryTreeNode<ExpressionTreeOp>>();//建立了一个存储BinaryTreeNode类的无序列表
        UnorderedListADT<Integer> levelList = new ArrayUnorderedList<Integer>();//建立一个存储int变量的无序列表

        BinaryTreeNode<ExpressionTreeOp> current;//设置指向无序列表的一个临时变量current
        String result = "";
        int printDepth = this.getHeight();//定义printDepth表示树的高度
        int possibleNodes = (int) Math.pow(2, printDepth + 1);//定义possibleNodes表示可能的树的结点数量多少,possibleNodes = 2 ^ (printDepth + 1)
        int countNodes = 0;

        nodes.addToRear(root);//把根结点添加到无序列表末尾
        Integer currentLevel = 0;
        Integer previousLevel = -1;//设置两个int变量,为接下来的换行操做进行判断
        levelList.addToRear(currentLevel);//currentLevel添加到levelList末尾

        while (countNodes < possibleNodes) {
            countNodes = countNodes + 1;
            current = nodes.removeFirst();//current为nodes移除的首位
            currentLevel = levelList.removeFirst();
            if (currentLevel > previousLevel) {
                result = result + "\n\n";//若是curreLevel大于previousLevel,就进行换行操做
                previousLevel = currentLevel;
                for (int j = 0; j < ((Math.pow(2, (printDepth - currentLevel))) - 1); j++)
                    result = result + " ";//换行以后添加空格
            } else {
                for (int i = 0; i < ((Math.pow(2,
                        (printDepth - currentLevel + 1)) - 1)); i++) {
                    result = result + " ";//不进行换行操做,代表是某一层中的中间的某一结点,在两个结点之间添加空格
                }
            }
            if (current != null) {
                result = result + (current.getElement()).toString();//将当前树的根结点加到result串中
                nodes.addToRear(current.getLeft());
                levelList.addToRear(currentLevel + 1);
                nodes.addToRear(current.getRight());
                levelList.addToRear(currentLevel + 1);//将前一个根结点的左右孩子都存入nodes的尾部,curreLevel+1存入levelList表示左右孩子是在根结点的下一行
            } else {
                nodes.addToRear(null);
                levelList.addToRear(currentLevel + 1);
                nodes.addToRear(null);
                levelList.addToRear(currentLevel + 1);
                result = result + " ";
            }

        }

        return result;
    }

我以为这部分代码难理解的地方在于为了输出时的图像接近于咱们通常的树的结构图而进行的换行操做和添加空格的操做。
判断某个结点在第几层时运用了一个levelList的一个列表,以及两个int变量currentLevel和previousLevel分别赋值为0和-1,经过currentLevel和previousLevel的关系判断是否须要换行,例如循环到第二层时,nodes里有了左右孩子两个结点,levelList里首位和第二位此时存储的都是1,而previousLevel此时为0,首先循环时currentLevel = levelList.removeFirst,currentLevel=1>previousLevel=0,因此须要另起一行result = result + "\n\n",以后将previousLevel赋为当前的currentLevel的值previousLevel = currentLevel;,current此时为nodes首位即左孩子,加入result字符串,再将左孩子的左右孩子分别加入列表尾,它们对应的levelList里的值为currentLevel+1=2,以后将列表头右孩子赋给current,此时的currLevel=previousLevel=1,因此走else这条路,不须要换行而后进行接下来的操做,若是右孩子为叶结点,虽然没有子结点,可是依然会留有存储空间nodes.addToRear(current.getLeft());,只不过里面存放的是null,因此输出时计算机走到此处会继续执行,并不断建立新的存储空间。因此在外面须要套一层while循环,由于二叉树前n层有不高于2^(n+1)个元素,因此定义possibleNodes的值为int possibleNodes = (int) Math.pow(2, printDepth + 1);
另外关于加空格的方法不是太懂,譬如为什么要这么定义j < ((Math.pow(2, (printDepth - currentLevel))) - 1)在两个结点之间添加空格。
关于二者的取值好像也不是那么严格,也就是不必定非要取0和-1,换成1和0,-1和-2之类也是能够的,只不过取值的不一样会影响树结构的宽窄,或者二者的差值也不必定须要为1,差2差3好像都没有问题,只不事后面levelList.addToRear(currentLevel + 1);的方法,对应就该加2或者加3了。数组

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

  • 问题1:运行expressionTree类的printTree方法时,树的结构没有所有打印出来,如图
    数据结构

  • 问题1解决方案:debug一下,发现我尚未完成getHeight方法,getHeight方法那儿仍是return 0;天然表示上会出错。返回height的方法我想应该分做三步,即判断树的结点分别是根结点、内部结点以及叶结点,首先判断根结点是否为空,不为空高度加一,以后用一个while循环以及PP10.3的方法反复判断接下的结点是否为内部结点,如果则高度加一,结点赋为该结点的左孩子,若判断出该结点为叶结点,则高度加一再返回高度值。
BinaryTreeNode cur = root;
        int height = 0;
        if (root != null){
            height++;
        }
        cur = cur.left;
        while (cur.judge() != true){
            height++;
            cur = cur.left;
        }
        height++;
        return height;

代码修改后的运行截图为
学习


  • 问题2:使用LinkedBinaryTree的toString方法时最终输出一串乱码

  • 问题2解决方案:在网上寻找解决方案,而后了解到数组是没有toString方法的。

    Object中的toString()方法,是将传入的参数的类型名和摘要(字符串的hashcode的十六进制编码)返回,直接对数组使用了toString()方法,就会获得 一个Ljava.lang.String;@175d6ab
    其中,Ljava.lang.String 是指数据是String类型的
    虽然这里给出的乱码信息是3ecf72fd,可是他给出的解决方案是使用循环的方法来输出数组中的每个值,姑且一试
    这里输出树的结构须要用到递归,而后输出时有点奇怪的就是我把右孩子放在其双亲的上面,左孩子放在双亲的下面,因此会有些不像树的形状

public void oString() {
        string(root);
    }

private void string(BinaryTreeNode temp){
        if(temp != null){
            string(temp.getRight());
            System.out.println(temp.getElement() + " ");
            string(temp.getLeft());
        }
    }

输出结果如图

凑合也能看

代码托管

上周考试错题总结

上周没有错题哦

结对及互评

  • 博客中值得学习的或问题:
    • 谭鑫的博客问题记录详细,并结合了各家之长,把几个优秀同窗的代码都作了对比分析
    • 方艺雯的博客学习过程记录清晰,关于树的几种遍历方法用了图片来说解,建议能够加上老师课堂上补充的关于遍历的知识
  • 基于评分标准,我给谭鑫的博客打分:8分。得分状况以下:
    正确使用Markdown语法(加1分):
    模板中的要素齐全(加1分)
    教材学习中的问题和解决过程, 三个问题加3分
    代码调试中的问题和解决过程, 三个问题加3分

  • 基于评分标准,我给方艺雯的博客打分:8分。得分状况以下:、
    正确使用Markdown语法(加1分):
    模板中的要素齐全(加1分)
    教材学习中的问题和解决过程, 两个问题加2分
    代码调试中的问题和解决过程, 四个问题加4分

  • 本周结对学习状况
  • 上周博客互评状况

其余

本周的学习状况还算不错,最困难的地方不在于几个PP项目,反而在于问题中提到的那个printTree的代码的理解。不过在考试中发挥得有点糟糕,不知道怎么的错了两道不应错的题

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 0/0 1/1 8/8
第二周 470/470 1/2 12/20
第三周 685/1155 2/4 10/30
第四周 2499/3654 2/6 12/42
第六周 1218/4872 2/8 10/52
第七周 590/5462 1/9 12/64

参考资料

相关文章
相关标签/搜索