课程:《程序设计与数据结构》
班级: 1723
姓名: 王禹涵
学号:20172323
实验教师:王志强
实验日期:2018年11月11日
必修/选修: 必修html
1-实现二叉树
参考教材p212,完成链树LinkedBinaryTree的实现(getRight,contains,toString,preorder,postorder)java
用JUnit或本身编写驱动类对本身实现的LinkedBinaryTree进行测试,提交测试代码运行截图,要全屏,包含本身的学号信息git
课下把代码推送到代码托管平台算法
2-中序先序序列构造二叉树
基于LinkedBinaryTree,实现基于(中序,先序)序列构造惟一一棵二㕚树的功能,好比给出中序HDIBEMJNAFCKGL和后序ABDHIEJMNCFGKL,构造出附图中的树数组
用JUnit或本身编写驱动类对本身实现的功能进行测试,提交测试代码运行截图,要全屏,包含本身的学号信息数据结构
课下把代码推送到代码托管平台并发
3-决策树
本身设计并实现一颗决策树函数
提交测试代码运行截图,要全屏,包含本身的学号信息源码分析
课下把代码推送到代码托管平台post
4-表达式树
输入中缀表达式,使用树将中缀表达式转换为后缀表达式,并输出后缀表达式和计算结果(若是没有用树,则为0分)
提交测试代码运行截图,要全屏,包含本身的学号信息
课下把代码推送到代码托管平台
5-二叉查找树
完成PP11.3
提交测试代码运行截图,要全屏,包含本身的学号信息
课下把代码推送到代码托管平台
6-红黑树分析
参考http://www.cnblogs.com/rocedu/p/7483915.html对Java中的红黑树(TreeMap,HashMap)进行源码分析,并在实验报告中体现分析结果。
(C:\Program Files\Java\jdk-11.0.1\lib\src\java.base\java\util)
第一部分没有什么难度,要求LinkedBinaryTree类实现的各类方法教材中都已经给出,因此只需编写测试类方法进行测试就好了。实验结果以下
第一张图测试了contains、toString方法,toString使用的是表达式树中的printTree方法
第二张图展现了getRight和前序后序输出方法的测试
第二部分解决的关键在如何经过中序和先序数组推出树的结构。首先先序输出由于是从树的根结点开始的,因此先序数组的第一个就是整个二叉树的根结点了。而后根据这个数到中序列表中去寻找对应的,以这个值为界,前半部分就是它的左子树,后半部分就是它的右子树。重复上述步骤就能够继续划分子树的结构直到结束。
这个方法首先判断前序和中序数组是否为空或者两个长度是否相等,若是为空或者不相等就抛出异常,不然就进行构造树的操做,首先取出前序列表第一个值,与中序列表进行比对,找到对应的值的位置,而后以此为界将它的左子树的前序中序数组和右子树的前序中序数组分别存入新的数组中进行递归操做,直到最后每个数组里只存有一个数时,调用二叉树的构造方法,就能够获得一棵二叉树。
结果如图
第三部分的内容很简单,参照书上代码背部疼痛诊断器的内容,修改其中的语句就能够了
这是实验最难的一部分。这一部分思考了好久,结合了不少资料。主要是须要解决带括号的问题,设置两个链表,一个存放String字符、一个存放树。经过split方法将中缀式断开,判断每个字符,若是是加减运算符,就把它存到String链表的末尾,若是是乘除运算符,就判断下一个字符是不是“(”,若是是的话,将括号里的运算式提出来,运用递归算法先将这一串小式子转换成二叉树,放入树的链表中,若是不是的话,就以当前字符的下一个做为新树的子树,当前字符为根结点,从树的链表中取出最末一位树做为左子树,造成一颗新树再存回树的链表中。若是是左括号,须要一直遍历接下来的符号,直到找到右括号,把中间的式子提出来单独构造新树。若是是数字,那么就直接放在树的数组中,看成只有根结点的树。最后经过迭代器的方法进行输出
这一部份内容就是运行PP11.3,没有什么能够记录的
红黑树
TreeMap的构造函数
// 默认构造函数。使用该构造函数,TreeMap中的元素按照天然排序进行排列。 TreeMap() // 建立的TreeMap包含Map TreeMap(Map<? extends K, ? extends V> copyFrom) // 指定Tree的比较器 TreeMap(Comparator<? super K> comparator) // 建立的TreeSet包含copyFrom TreeMap(SortedMap<K, ? extends V> copyFrom)
设置结点的颜色
private static final boolean RED = false; private static final boolean BLACK = true;
Entry相关函数
public Map.Entry<K,V> firstEntry() { return exportEntry(getFirstEntry()); } final Entry<K,V> getFirstEntry() { Entry<K,V> p = root; if (p != null) while (p.left != null) p = p.left; return p; }
FirstEntry和getFirstEntry都用于获取第一个结点,firstEntry是对外的,getFirstEntry是对内的。这样作是为了防止用户修改返回的Entry,同时,getFirstEntry返回的对象除了能够进行getKey、getValue的操做以外,还能够经过setValue修改图。
Key相关函数
ceilingKey(K key)的做用是“返回大于/等于key的最小的键值对所对应的KEY,没有的话返回null”
public K ceilingKey(K key) { return keyOrNull(getCeilingEntry(key)); }
ceilingKey()是经过getCeilingEntry()实现的。keyOrNull()是获取节点的key,没有的话,返回null。
static <K,V> K keyOrNull(TreeMap.Entry<K,V> e) { return e == null? null : e.key; }
getCeilingEntry(K key)的做用是“获取TreeMap中大于/等于key的最小的节点,若不存在(即TreeMap中全部节点的键都比key大),就返回null”。
遍历方法
遍历的方式包括遍历TreeMap的键值对(entrySet),遍历TreeMap的键(KeySet)、遍历TreeMap的值(value)三种。
经过keyIterator() 和 descendingKeyIterator()来讲明,keyIterator()的做用是返回顺序的KEY的集合,
descendingKeyIterator()的做用是返回逆序的KEY的集合。
Iterator<K> keyIterator() { return new KeyIterator(getFirstEntry()); } final class KeyIterator extends PrivateEntryIterator<K> { KeyIterator(Entry<K,V> first) { super(first); } public K next() { return nextEntry().key; } }
HashMap
HashMap由数组+链表组成的,数组是HashMap的主体.
HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每个Entry包含一个key-value键值对。
构造函数
public HashMap(int initialCapacity, float loadFactor) { //此处对传入的初始容量进行校验,最大不能超过MAXIMUM_CAPACITY = 1<<30(230) if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; threshold = initialCapacity; init();//init方法在HashMap中没有实际实现,不过在其子类如 linkedHashMap中就会有对应实现 }
put操做
public V put(K key, V value) { //若是table数组为空数组{},进行数组填充(为table分配实际内存空间),入参为threshold,此时threshold为initialCapacity 默认是1<<4(24=16) if (table == EMPTY_TABLE) { inflateTable(threshold); } //若是key为null,存储位置为table[0]或table[0]的冲突链上 if (key == null) return putForNullKey(value); int hash = hash(key);//对key的hashcode进一步计算,确保散列均匀 int i = indexFor(hash, table.length);//获取在table中的实际位置 for (Entry<K,V> e = table[i]; e != null; e = e.next) { //若是该对应数据已存在,执行覆盖操做。用新value替换旧value,并返回旧value Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++;//保证并发访问时,若HashMap内部结构发生变化,快速响应失败 addEntry(hash, key, value, i);//新增一个entry return null; }
在常规构造器中,没有为数组table分配内存空间(有一个入参为指定Map的构造器例外),而是在执行put操做的时候才真正构建table数组
实验二-1-实现二叉树
实验二 树-2-中序先序序列构造二叉树
实验二 树-3-决策树
实验二 树-4-表达式树
实验二 树-5-二叉查找树
这实验,简单的简单难的难,那个红黑树的源码分析若是没有资料帮扶,我真多是束手无策,反正有些难受
《Java程序设计与数据结构教程(第二版)》
《Java程序设计与数据结构教程(第二版)》学习指导