2018年学习总结博客总目录:第一周 第二周 第三周 第四周 第五周 第六周
html
1.树的概述及基本概念
(1)树是一种非线性数据结构,其中的元素被组织成了一个层次结构.
(2)树由一个包含结点和边的集构成,其中元素被存储在这些结点中,边则将一个结点和另外一个结点链接起来。每一结点都位于该树层次结构中的某一特顶层上。
(3)树的根式位于该树顶层的惟一结点。一棵树只有一个根结点。
(4)位于树中较底层的结点是上一层的孩子。一个结点只有一个双亲,但一个结点能够有不少孩子。同一双亲的两个结点互称兄弟(sibling)
(5)根结点是惟一没有双亲的结点。没有任何孩子的结点称为叶子,一个至少有一个孩子的非根结点称为一个内部节点。
(6)一个结点在从根开始的路径中位于另外一结点之上,那么该结点就是它的祖先。根是树中全部节点的最终祖先。沿着起始自某一特定结点的路径能够到达的结点是该结点的子孙(descendant)。
(7)结点的层也就是从根结点到该结点的路径长度。经过计算从根到该结点所必须穿过的边的数目,便是路径长度。根位于0层。
(8)树的高度是指从根到叶子之间最远路径的长度。
2.树的分类
树有多种分类方式,最重要的一个是树中任一结点能够具备的最大孩子的数目。这个值也称为树的度(order)。java
一些概念node
彻底树
若某树是平衡的,且底层全部叶子都位于树的左边,则该树是彻底的。git
满树
若是一棵n元树的全部叶子都位于同一层且每一结点要么是一片叶子要么正好具备n个孩子,则该树是满的。算法
2.实现树的策略
(1)树的数组实现之计算策略
对于二叉树而言,一种策略是:对于任何存储在数组位置n处的元素而言,该元素的左孩子存储在位置(2n+1)处,右孩子存储在(2(n+1))处。但这样作会出现大量浪费存储空间的情况,好比这棵树不是彻底的或只是相对彻底的。数组
(2)树的数组实现之连接策略
如图:
数据结构
3.树的遍历
对于这样一棵树,咱们对它进行四种遍历
函数
(1)前序遍历:从根节点开始,访问每一结点及其孩子
访问顺序为:A→B→D→E→C
伪代码为学习
Visit node Traverse(left child) Traverse(right child)
(2)中序遍历:从根结点开始,访问结点的左孩子,而后是该结点,最后是任何剩余结点
访问顺序为:D→B→E→A→C
伪代码为:测试
Traverse(left child) Visit node Traverse(right child)
(3)后序遍历:从根结点开始,访问节点的孩子,而后是该结点
访问顺序为:D→E→B→C→A
伪代码为:
Traverse(left child) Traverse(right child) Visit node
(4)层序遍历:从根结点开始,访问每一层的全部结点,一次一层
访问顺序为:A→B→C→D→E
4.二叉树
二叉树是树的特殊一种,具备以下特色:一、每一个结点最多有两颗子树,结点的度最大为2。二、左子树和右子树是有顺序的,次序不能颠倒。三、即便某结点只有一个子树,也要区分左右子树。
二叉树的性质:
性质1:在二叉树的第i层上至多有2^i-1个结点(i>=1)。
性质2:深度为k的二叉树至多有2^k-1个结点(k>=1)。
性质3:对任何一颗二叉树T,若是其终端结点数为n0,度为2的 结点 数为n2,则n0 = n2+1.
性质4:具备n个结点的彻底二叉树深度为[log2n]+1 ([x]表示不 大于 x的最大整数)。
性质5:若是对一颗有n个结点的彻底二叉树(其深度为[log2n]+1) 的结点按层序编号(从第1层到第[log2n]+1层,每层从左到右),对任意一个结点i(1<=i<=n)有:
1).若是i=1,则结点i是二叉树的根,无双亲;若是i>1,则其双亲是结 点[i/2]
2).若是2i>n,则结点i无左孩子(结点i为叶子结点);不然其左孩 子是结点2i。
3).若是2i+1>n,则结点i无右孩子;不然其右孩子是结点2i+1。
import java.util.Iterator; public interface BinaryTreeADT<T> { public T getRootElement();//返回二叉树根引用指向的元素 public boolean isEmpty();//断定该树是否为空 public int size();//断定树中元素数目 public boolean contains(T targetElement);//断定指定目标是否在该树中 public T find(T targetElement);//若是找到指定元素,则返回指向其的引用 public String toString();//返回树的字符串表示 public Iterator<T> iterator(); public Iterator<T> iteratorInOrder();//为树的中序遍历返回一个迭代器 public Iterator<T> iteratorPreOrder(); //为树的前序遍历返回一个迭代器 public Iterator<T> iteratorPostOrder();//为树的后序遍历返回一个迭代器 public Iterator<T> iteratorLevelOrder();//为树的层序遍历返回一个迭代器 }
5.使用二叉树:表达式树、背部疼痛诊断器
6.用链表实现二叉树
先要构建一个BinaryTreeNode类,它负责跟踪存储在每一个位置上的元素,以及指向每一个结点的左右子树或孩子的指针。而后构建实现了BinaryTreeADT接口的LinkedBinaryTree类,须要跟踪位于树的根节点,以及树的元素数目。
问题1:关于书上代码很难理解,尤为是Expressiontree类下的printTree()方法。
public String printTree() { UnorderedListADT<BinaryTreeNode<ExpressionTreeOp>> nodes =new ArrayUnorderedList<BinaryTreeNode<ExpressionTreeOp>>(); UnorderedListADT<Integer> levelList =new ArrayUnorderedList<Integer>(); BinaryTreeNode<ExpressionTreeOp> current; String result = ""; int printDepth = this.getHeight()-1; 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; }
第一次循环:countNodes=1;current=“+”所在结点,currentLevel = 0,此时currentLevel>previousLevel,result = “\n\n”,previousLevel = 0,进入循环,循环一次,result = “\n\n空”,而后current不等于null,进入,得result=“\n\n空+”,nodes中元素1所在结点、1所在结点,levelList中元素一、1。
第二次循环:countNodes=2;current=“1”所在结点,currentLevel = 1,此时currentLevel>previousLevel,result = “\n\n\空+\n\n”,previousLevel = 1,不能进入循环,result = “\n\n\n\n空+\n\n”,而后current不等于null,进入,得result=“\n\n\n\n空+\n\n1”,nodes中1所在结点、null、null,levelList中元素一、二、2。
第三次循环:countNodes=3;current=“1”所在结点,currentLevel = 1,此时currentLevel不大于previousLevel,故调用else中代码,循环次数1次,得result=“\n\n\n\n空+\n\n1空”,而后current不等于null,进入,得result=“\n\n\n\n空+\n\n1空1”,nodes中null、null、null、null,levelList中元素二、二、二、2。
第四次循环:countNodes=4;current =null,currentLevel = 2,此时currentLevel>previousLevel,result=“\n\n空+\n\n1空1\n\n”,previousLevel = 2,不能进入循环,此时current等于null,故调用else中代码,nodes中null、null、null、null、null,levelList中元素二、二、二、三、3。result=“\n\n空+\n\n1空1\n\n空”。
第五次,不能进入循环,输出结果:result=“\n\n空+\n\n1空1\n\n空”,即result=“\n\n +\n\n1 1\n\n ”。IDEA运行结果如图,与计算一致。
循环完成以后,就该试着解释每一个变量的在整个打印树中的做用,nodes是用做存储树中各个结点所指向的元素,levelList是用做存储各个元素它所对应的层数位置变量,currentLevel和previousLevel共同在决定着是否该换行 ,便是否该跳至下一行。第一个if-else语句中循环是用做控制着空格的打印,以及该空几格。第二个if-else语句是在将该结点的子结点及其层数分别添加到nodes和levelList中去。
问题2:在运行背部疼痛诊断器时,老是出错,后面才发现循环不能进行。
if (scan.nextLine().equalsIgnoreCase("N"))current = current.getLeft(); else current = current.getRight();
运行后current.size()变为1,这里能够看出应该是getLeft和getRight方法有问题,因而找到LinkedBinaryTree中的对应方法:public LinkedBinaryTree<T> getLeft() { return new LinkedBinaryTree(this.root.getLeft()); } public LinkedBinaryTree<T> getRight() { return new LinkedBinaryTree(this.root.getRight()); }
这个代码是从那里直接拿过来的,觉得它是对的,也就没有改动,再看才发现问题,按照这个代码至关因而在取出root的左右孩子的结点元素,而后再拿它建立一个新的LinkedBinaryTree类的对象,这就是没有了以前的树的结构了,只是单单取出这一个结点元素建立新对象返回。而这样,显然,它是没有以前本该有的孩子了。知道了是这样,就把这个方法作了修改,如图,(是修改的那部分)
protected LinkedBinaryTree<T> left,right;//定义两个LinkedBinaryTree类型的初始变量left、right // 修改构造函数,初始化两个变量 public LinkedBinaryTree(T element, LinkedBinaryTree<T> left, LinkedBinaryTree<T> right) { root = new BinaryTreeNode<T>(element); root.setLeft(left.root); root.setRight(right.root); this.left = left; this.right = right; } //修改后的方法 public LinkedBinaryTree<T> getLeft() { return left; } public LinkedBinaryTree<T> getRight() { return right; }
改完以后,问题就解决了。
问题1:书上的“使用二叉树:表达式树”这里想要运行书上的代码,须要本身在LinkedBinaryTree类中添加getHeight()方法,遇到的问题见下:
问题1解决方案:在我补全了getHeight()方法后,再去运行程序,始终会抛出错误StackOverflowError,查API文档
注意到了“当应用程序递归太深而发生堆栈溢出时,抛出该错误”,但不明白什么叫递归太深。就又搜索了这个错误。
StackOverflowError是因为当前线程的栈满了 ,也就是函数调用层级过多致使。
好比死递归。
以上是网上查阅的资料。
死递归,这个知道一些,就是递归没法挑出来,if条件给的不对,因而就从递归下手,认真看了两遍,找到了错误。
圈红地方就是出错的地方,每次的递归我都是在用root这个,根本没法跳出递归,将root改成node,便可运行。
问题2:在测试contains方法时,它始终的结果是false,明明包含该元素也会显示为false。
问题2解决方案:如图,进行了单步调试,能够看出next.getElement()和targetElement中元素均为“+”,但是却断定条件不成立。
抱着试一试的心态,就把next.getElement()和targetElement都使用了String.valueOf(),输出的就是true了,多是由于以前的元素类型不一样吧,或者能够在next.getElement()前加个强制类型转换,转换为T,试了一下返回的结果又变回false了,有点奇怪。
上周代码行数为11412行,如今为12499行,本周共1087行。
上周未进行考试
博客中值得学习的或问题:
结对学习内容:第十章——树。
树这一章能够感受是到目前为止学起来最为困难的一章,一开始看表达式树时,整个都是混乱的,由于它调用后面的链表建立树,包括表达式树中的方法,理解起来都有困难。后面靠看着UML类图,对整个结构才算明白了一些,而后才去试着把每一个类下面的方法弄明白。最为困惑的就是背部疼痛诊断器,它是从文件中读取,而后输出,但是我弄了很久它就只能输出一个问题,循环不了,这里仍是有问题。下一章依然与树有关,但愿能把树这一部分彻底弄懂。
同时本周对博客园的博客样式进行了较大的改动。不改感受很差看,改起来又很麻烦,最后改这一次了。
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 0/0 | 1/1 | 15/15 | |
第二周 | 572/572 | 1/2 | 16/31 | |
第三周 | 612/1184 | 1/3 | 13/44 | |
第四周 | 1468/2652 | 2/5 | 13/57 | |
第五周 | 1077/3729 | 1/6 | 14/71 | 初步理解各个排序算法 |
第六周 | 1087/4816 | 1/7 | 17/88 | 认识树结构 |