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

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

教材学习内容总结

  • 概述
    • 树是一种非线性结构,其中的元素被组织成一个层次结构。
    • 树由一个包含结点(node)和(edge)的集构成,其中的元素被存储在这些结点中,边则将一个结点和另外一个结点链接起来。每一结点都位于该数层次结构中的某一特定层上,树的根就是那个位于该树顶层的惟一结点。一棵树只有一个根结点。
    • 基本术语:
      • 结点:存储数据元素和指向子树的连接,由数据元素和构造数据元素之间关系的引用组成。
      • 孩子结点:树中一个结点的子树的根结点称为这个结点的孩子结点,如图1中的A的孩子结点有B、C、D
      • 双亲结点:树中某个结点有孩子结点(即该结点的度不为0),该结点称为它孩子结点的双亲结点,也叫前驱结点。双亲结点和孩子结点是相互的,如图1中,A的孩子结点是B、C、D,B、C、D的双亲结点是A。
      • 兄弟结点:具备相同双亲结点(即同一个前驱)的结点称为兄弟结点,如图1中B、B、D为兄弟结点。
      • 结点的度:结点全部子树的个数称为该结点的度,如图1,A的度为3,B的度为2。
      • 树的度:树中全部结点的度的最大值称为树的度,如图1的度为3。
      • 叶子结点:度为0的结点称为叶子结点,也叫终端结点。如图1的K、L、F、G、M、I、J
      • 分支结点:度不为0的结点称为分支结点,也叫非终端结点。如图1的A、B、C、D、E、H
      • 结点的层次:从根结点到树中某结点所经路径的分支数称为该结点的层次。根结点的层次通常为1(也能够本身定义为0),这样,其它结点的层次是其双亲结点的层次加1。
      • 树的深度:树中全部结点的层次的最大值称为该树的深度(也就是最下面那个结点的层次)
      • 有序树和无序树:树中任意一个结点的各子树按从左到右是有序的,称为有序树,不然称为无序树。
      • 咱们将每一节点限制为不超过n个孩子的树称为n叉树。
      • 若是说树的全部叶子都位于同一层或者至少是彼此相差不超过一个层,就称之为平衡的。
      • 若是某树是平衡的,且底层全部叶子都位于树的左边,则认为该树是彻底的。
  • 树的数组实现之计算策略
    • 树的数组实现之计算策略:
      • 一种可能的计算策略是将元素n的左孩子置于位置(2 * n-1),将右孩子置于位置(2 *(n+1))
      • 优势:咱们能够依照容量对其进行管理,这与咱们用数组实现列表、队列和栈且对其进行容量管理时的方式基本同样。
      • 缺点:若是存储的树不是彻底的或只是相对彻底的,则该数组会为不包括数据的树位置分配空间,浪费了大量的存储空间。
    • 树的数组实现之模拟连接策略:
      • 模拟连接策略容许连续分配数组位置而不用考虑该树的彻底性。
      • 该数组的每一元素都是一个结点类,每一节点存储的是每一孩子(可能还有其双亲)的数组索引,而不是做为指向其孩子(可能还有其双亲)指针的对象引用变量。
      • 优势:这种方式是的元素可以连续存储在数组中,所以不会浪费空间。
      • 缺点:该方式增长了删除树中元素的成本,由于它要么须要对生育元素进行移位以维持连续状态,要么须要保留一个空闲列表。
    • 通常而言,一棵含有m各元素的平衡n元树具备的高度为lognm。
  • 树的遍历
    • 前序遍历: 从根结点开始,访问每一结点及其孩子。
    Visit node
    Traverse(left child)
    Traverse(right child)
    • 中序遍历: 从根节点开始,访问结点的左孩子,而后是该结点,再而后是任何剩余结点。
    Traverse(left child)
    Visit node
    Traverse(right child)
    • 后序遍历: 从根结点开始,访问结点的孩子,而后是该结点。
    Traverse(left child)
    TRaverse(right child)
    Visit node
    • 层序遍历: 从根结点开始,访问每一层的全部结点,访问每一层的全部结点,一次一层。
    Creat a queue called nodes
    Create an unordered list calles results
    Enqueue the root onto the nodes queue
    while the nodes queue is not empty
    {
        Dequeue the first element from the queue
        If that element is not null
           Add that element to the rear of the results list
           Enqueue the children of the element on the nodes queue
         Else
            Add null on the result list
    
    }
    Return an iterator for the result list
  • 二叉树
操做 最差时间复杂度
getRoot 返回指向二叉树根的引用 
isEmpty 断定该树是否为空 
size 断定树中的元素数目 
contains 断定指定目标是否在该树中 
find 若是找到指定元素,则返回指向其的引用 
toString 返回树的字符串表示 
itertorInOrder 为树的中序遍历返回一个迭代器 
itertorPreOrder 为树的前序遍历返回一个迭代器 
iteratorPostOrder 为树的后序遍历返回一个迭代器 
iteratorLevelOrder 为树的层序遍历返回一个迭代器 
  • 二叉树的性质
    • 若二叉树的根节点位于第一层
      • 在二叉树的第i层最多有2i-1个结点。
      • 深度为k的二叉树最多有2k-1个结点。
      • 对任何一棵二叉树,若是其叶结点个数为n0,度为2的结点数为n2则有n0=n2+1。
    • 两种特殊的二叉树

      html

    • 彻底二叉树的性质
      • 具备n个结点的彻底二叉树的高度为log2n(下取整)+1
      • 若是将一棵有n个结点的彻底二叉树自顶向下,同一层自左向右给结点编号1,2,3......n,则对于任意结点(1<=i<=n),有:
        • 若i= 1,则该i结点是树根,它无双亲;
        • 若2i>n,则编号为i的结点无左孩子,不然它的左孩子是编号为2*i的结点;
        • 若2i+1>n,则编号为i的结点无右孩子,不然其右孩子结点编号为2*i+1;

    • 二叉树的二叉链表存储结构。二叉链表结构主要由一个数据域和两个分别指向左、右孩子的结点组成,其结构以下:
    BinaryNode(T data , BinaryNode<T> left , BinaryNode<T> right )

    • 二叉树的三叉链表存储结构。三叉链表主要是在二叉链表的基础上多添加了一个指向父结点的域,这样咱们就存储了父结点与孩子结点的双向关系,固然这样也增长了必定的空开销其结点结构以下:
    ThreeNode(T data ,ThreeNode<T> parent,ThreeNode<T> left,ThreeNode<T> right)

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

  • 问题一:在树中Expression中有这样一段代码(以下图代码),由二叉树的性质可知,一棵二叉树当满树的状况时它的结点总数最多为2^(depth+1)-1,而书上在这里的int型变量posssibleNodes变量为2^(depth+1)-1,但在实际进行操做的时候并不能保证该二叉树是满树,那么为何可使用“countNodes < possibleNodes”做为循环条件进行遍历呢?
UnorderedListADT<BinaryTreeNode<ExpressionTreeOp>> nodes =
            new ArrayUnorderedList<BinaryTreeNode<ExpressionTreeOp>>();
        UnorderedListADT<Integer> levelList =
            new ArrayUnorderedList<Integer>();
        BinaryTreeNode<ExpressionTreeOp> current;
        String result = "";
        int printDepth = this.getHeight();
        int possibleNodes = (int)Math.pow(2, printDepth + 1);
        int countNodes = 0;

        nodes.addToRear(root);
        Integer currentLevel = 0;
        Integer previousLevel = -1;
        levelList.addToRear(currentLevel);

        while (countNodes < possibleNodes)
        {
            countNodes = countNodes + 1;
            current = nodes.removeFirst();
            currentLevel = levelList.removeFirst();
            if (currentLevel > previousLevel)
            {
                result = result + "\n\n";
                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();
                nodes.addToRear(current.getLeft());
                levelList.addToRear(currentLevel + 1);
                nodes.addToRear(current.getRight());
                levelList.addToRear(currentLevel + 1);
            }
            else {
                nodes.addToRear(null);
                levelList.addToRear(currentLevel + 1);
                nodes.addToRear(null);
                levelList.addToRear(currentLevel + 1);
                result = result + " ";
            }

        }

        return result;
    }
  • 问题一解决方案:咱们先来分析每一行代码的做用(第一次看到上面这坨代码,有那么多循环,真使人头皮发麻),单用眼看怕是很难看出来,最简单直接的方法就是run一下看运行出来的结果进行对比。先来run一下本来的代码:

    OK,没有问题,很是完美。而后注释掉第一个for循环

    咱们能够清楚的看到左边的空白都没有了,很明显,第一个for循环是为了打印每一行最前面的空白来保证“树”的形状,咱们再来注释掉第二个循环

    一样的,咱们能够很明显的看出每一行的两结点处的留白都没有了,如今咱们弄明白了两个for循环的做用,免去了它们的干扰,接着咱们回过头来看循环条件的问题。咱们首先要明确的是这里possibleNodes的值,因为该程序中height是从一开始算的,因此这里的possibleNodes值应该是2的五次方,按照问题里个人想法,这个值的数目应该是远远大于所须要的限定值,咱们先适当的缩小possibleNodes的范围看看结果会发生什么变化。(对possibleNodes-1)

    结果并无发生什么变化,这就说明possibleNodes的值确实是偏大的。该二叉树中共有九个元素因此理论上咱们只须要九次循环就足够,因此把possibleNodes减去(32-9)(简单的数学问题)23次再试试!


    此次就和预期的结果不太同样了,应该是possibleNodes再减去一的时候树才会发生变化才对,为何如今结点就减小了呢?这时,我发现,树的最下面一层结点靠右侧,在代码表示时是预留了空间的,分别测试possibleNodes为15和14时的状况


      如上图所示,跟预期的结果同样,因此综上所述,possibleNodes的限定范围确实是过大的,可是在循环完结点后,剩下的循环次数为打印空白,因此在视觉效果上就跟没有执行同样!
  • 问题二:ExpressionTreeOp类中定义了一个int型变量termType,在判断一个结点储存的element是都为操做符时返回“termType==1”,对这行代码不是特别明白。
  • 问题二解决方案:紧接着教材的学习,有这样的一段代码
if ((operator == '+') || (operator == '-') || (operator == '*') || 
                 (operator == '/'))
            {
                operand1 = getOperand(treeStack);
                operand2 = getOperand(treeStack);
                treeStack.push(new ExpressionTree 
                                  (new ExpressionTreeOp(1,operator,0), operand2, operand1));
            }
            else
            {
                treeStack.push(new ExpressionTree(new ExpressionTreeOp
                                    (2,' ',Integer.parseInt(tempToken)), null, null));
            }

原来,termType的用法是接收传入的形参,判断传入的形参是否为1以此来返回ture或者false来判断是否为操做符。java

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

  • 问题1:在对方法进行测试时,发现使用toString时会出现空指针的状况
  • 问题一解决方法:我仔细阅读了报错给的提示,发现最终是个人ArrayList中的find方法存在毛病,由于所比较的数据类型为T,因此在判断两个元素是否相等的时候不能使用“equals”,更改为“==”问题就得解决掉了。

代码托管

上周考试错题总结

  这周没有错题哦~node

结对及互评

  • 博客中值得学习的或问题:
    • 排版精美,对教材的总结细致,善于发现问题,对于问题研究得很细致,解答也很周全。
  • 代码中值得学习的或问题:
    • 代码写的很规范,思路很清晰,继续加油!

点评过的同窗博客和代码

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

  这周的内容相比前面几周所学习的内容来讲难了许多,从代码的理解难易程度上就可以感受的出来,这周也花费了我更多的实践,同时也给本身敲了一个警钟,本身不会和不清楚的只是还有不少,不能再像以前那样佛系学习,要更加的努力才是,但愿能在之后的学习生活中继续进步!git

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
第一周 200/200 1/1 5/20
第二周 981/1181 1/2 15/20
第三周 1694/2875 1/3 15/35
第四周 3129/6004 1/4 15/50
第五周 1294/7298 1/5 15/65
第六周 1426/8724 1/6 20/85
  • 计划学习时间:20小时web

  • 实际学习时间:20小时算法

参考资料

相关文章
相关标签/搜索