课程:程序设计与数据结构
班级: 1623
姓名: 石亚鑫
学号:20162303javascript
成绩: 2分
指导教师:娄嘉鹏 王志强
实验日期:10月23日html
实验密级: 非密级
预习程度: 已预习
实验时间:15:25-17:15java
必修/选修: 必修
实验序号: cs_03node
完成链树LinkedBinaryTree的实现(getRight,contains,toString,preorder,postorder)
用JUnit或本身编写驱动类对本身实现的LinkedBinaryTree进行测试,提交测试代码运行截图数组
基于LinkedBinaryTree,实现基于(中序,先序)序列构造惟一一棵二㕚树的功能,好比教材P372,给出HDIBEMJNAFCKGL和ABDHIEJMNCFGKL,构造出附图中的树
用JUnit或本身编写驱动类对本身实现的功能进行测试,提交测试代码运行截图数据结构
完成PP16.6
提交测试代码运行截图,要全屏,包含本身的学号信息
课下把代码推送到代码托管平台源码分析
完成PP16.8
提交测试代码运行截图,要全屏,包含本身的学号信息
课下把代码推送到代码托管平台post
完成PP17.1
提交测试代码运行截图,要全屏,包含本身的学号信息
课下把代码推送到代码托管平台学习
6.参考http://www.cnblogs.com/rocedu/p/7483915.html对Java中的红黑树(TreeMap,HashMap)进行源码分析,并在实验报告中体现分析结果测试
public String toString() { String result = ""; ArrayIterator <T> iter = new ArrayIterator <T> (); root.postorder ( iter ); return result+iter; } public Iterator<T> preorder() { ArrayIterator <T> iter = new ArrayIterator <T> (); if (root != null) root.preorder ( iter ); return iter; } public Iterator<T> postorder() { ArrayIterator <T> iter = new ArrayIterator <T> (); if (root != null) root.postorder ( iter ); return iter; }
前序和后序与中序比较类似,调换一下顺序就能够了,toString原本应该按照层序遍历打印,可是层序遍历我暂时没法实现,因而就先打印后序。
public class TwentyQuestions { private LinkedBinaryTree<String> tree; public TwentyQuestions() { String e1 = "你以为本身可以吃苦吗?"; String e2 = "你以为本身有耐心吗?"; String e3 = "你有远大的目标吗?"; String e4 = "你喜欢钻研吗?"; String e5 = "但愿你用你的耐心专一于一件事情并最终完成。"; String e6 = "你以为你的目标容易实现吗?"; String e7 = "你有短时间目标吗?"; String e8 = "若是你拥有这些,那么好好坚持,吃得苦中苦,方为人上人。"; String e9 = "实现目标刻苦钻研是必选的,加油!"; String e10 = "一个好的目标是须要尽本身的努力来实现的,若是目标能垂手可得的实现,那绝对不是一个好的目标。"; String e11 = "既然目标不是那么容易实现,那么你更应该努力实现,而不是自暴自弃。"; String e12 = "你拥有短时间目标却不愿吃苦实现它,那么你的短时间目标只是空话。"; String e13 = "你有梦想吗?"; String e14 = "有梦想是好事,可是毫不能空想,要为实现目标付出努力。"; String e15 = "你愿意作出一些改变吗?"; String e16 = "那么从如今开始改变,确立一个小目标并努力实现,一步一个脚印,加油!"; String e17 = "你以为一我的的一辈子必定要作成某一件事情吗?"; String e18 = "既然你心中依然有着想法,那么但愿你能为此改变,无论何时,只要想改就不会晚。"; String e19 = "有的时候平平淡淡也算一种生活方式,只要回首往事时可以对得起本身的心里就能够了。"; LinkedBinaryTree<String> n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14,n15,n16,n17,n18,n19; n18 = new LinkedBinaryTree<String>(e18); n19 = new LinkedBinaryTree<String>(e19); n17 = new LinkedBinaryTree<String>(e17, n18, n19); n16 = new LinkedBinaryTree<String>(e16); n15 = new LinkedBinaryTree<String>(e15, n16, n17); n14 = new LinkedBinaryTree<String>(e14); n13 = new LinkedBinaryTree<String>(e13, n14, n15); n12 = new LinkedBinaryTree<String>(e12); n7 = new LinkedBinaryTree<String>(e7, n12, n13); n8 = new LinkedBinaryTree<String>(e8); n9 = new LinkedBinaryTree<String>(e9); n4 = new LinkedBinaryTree<String>(e4, n8, n9); n10 = new LinkedBinaryTree<String>(e10); n11 = new LinkedBinaryTree<String>(e11); n6 = new LinkedBinaryTree<String>(e6, n10, n11); n5 = new LinkedBinaryTree<String>(e5); n2 = new LinkedBinaryTree<String>(e2, n4, n5); n3 = new LinkedBinaryTree<String>(e3, n6, n7); tree = new LinkedBinaryTree<String>(e1, n2, n3); } public void diagnose() { Scanner scan = new Scanner(System.in); BinaryTree<String> current = tree; System.out.println ("你要根据现实状况作出选择:"); while (current.size() > 1) { System.out.println (current.getRootElement()); if (scan.nextLine().equalsIgnoreCase("Y")) current = current.getLeft(); else current = current.getRight(); } System.out.println (current.getRootElement()); } }
跟本来代码比较类似,改一下字符串和树的构造顺序就能够了
实现以下:
在查找数上实现findMin和findMax方法,最小值就从根一直到最左,最大值就是从根一直到最右。
public T findMin() { BTNode<T> node = null; if (root != null){ node = node.getLeft();} return (T) node; } public T findMax() { BTNode<T> node = null; if (root != null){ node = node.getRight();} return (T) node; }
HashMap继承自AbstractMap,实现了Map接口。
Map接口定义了全部Map子类必须实现的方法。Map接口中还定义了一个内部接口Entry(为何要弄成内部接口?改天还要学习学习)。Entry将在后面有详细的介绍。
AbstractMap也实现了Map接口,而且提供了两个实现Entry的内部类:SimpleEntry和SimpleImmutableEntry。
定义了接口,接口中又有内部接口,而后有搞了个抽象类实现接口,抽象类里面又搞了两个内部类实现接口的内部接口
/** * 默认的初始容量,必须是2的幂。 */ static final int DEFAULT_INITIAL_CAPACITY = 16; /** * 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换) */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * 默认装载因子,这个后面会作解释 */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * 存储数据的Entry数组,长度是2的幂。看到数组的内容了,接着看数组中存的内容就明白为何博文开头先复习数据结构了 */ transient Entry[] table; /** * map中保存的键值对的数量 */ transient int size; /** * 须要调整大小的极限值(容量*装载因子) */ int threshold; /** *装载因子 */ final float loadFactor; /** * map结构被改变的次数 */ transient volatile int modCount; 接着是HashMap的构造方法。 /** *使用默认的容量及装载因子构造一个空的HashMap */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);//计算下次须要调整大小的极限值 table = new Entry[DEFAULT_INITIAL_CAPACITY];//根据默认容量(16)初始化table init(); } /** * 根据给定的初始容量的装载因子建立一个空的HashMap * 初始容量小于0或装载因子小于等于0将报异常 */ public HashMap(int initialCapacity, float loadFactor) { 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); int capacity = 1; //设置capacity为大于initialCapacity且是2的幂的最小值 while (capacity < initialCapacity) capacity <<= 1; this.loadFactor = loadFactor; threshold = (int)(capacity * loadFactor); table = new Entry[capacity]; init(); } /** *根据指定容量建立一个空的HashMap */ public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR);//调用上面的构造方法,容量为指定的容量,装载因子是默认值 } /** *经过传入的map建立一个HashMap,容量为默认容量(16)和(map.zise()/DEFAULT_LOAD_FACTORY)+1的较大者,装载因子为默认值 */ public HashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); putAllForCreate(m); }
TreeMap有Values、EntrySet、KeySet、PrivateEntryIterator、EntryIterator、ValueIterator、KeyIterator、DescendingKeyIterator、NavigableSubMap、AscendingSubMap、DescendingSubMap、SubMap、Entry共十三个内部类。
Values
// 从类的定义能够看出,Values是一个集合类
class Values extends AbstractCollection<V> { // 提供集合类Values的迭代器 public Iterator<V> iterator() { return new ValueIterator(getFirstEntry()); } // 返回TreeMap中保存的节点数 public int size() { return TreeMap.this.size(); } // 判断TreeMap中是否存在Value为o的节点 public boolean contains(Object o) { return TreeMap.this.containsValue(o); } // 删除一个对象 public boolean remove(Object o) { // 遍历TreeMap for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e)) { // 寻找值相等的节点 if (valEquals(e.getValue(), o)) { // 删除找到的节点 deleteEntry(e); return true; } } return false; } // 清空TreeMap public void clear() { TreeMap.this.clear(); } }
Values类其实是一个代理,多数方法都是调用TreeMap的方法。在Values的iterator()方法中返回了一个ValuesIterator对象,下面来看和迭代器相关的内部类。PrivateEntryIterator是TreeMap中和迭代器相关的类的基础,如下是PrivateEntryIterator的内容。
abstract class PrivateEntryIterator<T> implements Iterator<T> { // 指向next的引用 Entry<K,V> next; // 保留对上一次返回节点的引用 Entry<K,V> lastReturned; int expectedModCount; // 构造方法,lastReturned置空,next指向传入的节点 PrivateEntryIterator(Entry<K,V> first) { expectedModCount = modCount; lastReturned = null; next = first; } // 判断是否还有下一个节点 public final boolean hasNext() { return next != null; } // 返回下一个节点 final Entry<K,V> nextEntry() { Entry<K,V> e = next; if (e == null) throw new NoSuchElementException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); // next移动到它的继承者 next = successor(e); // 记录被返回的节点 lastReturned = e; // 返回原先记录的next节点 return e; } // 前一个节点 final Entry<K,V> prevEntry() { Entry<K,V> e = next; if (e == null) throw new NoSuchElementException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); // 获取指定节点的“前任”(按遍历次序的前一个节点) next = predecessor(e); // 记录被返回的节点 lastReturned = e; return e; } // 移除最近一次被返回的节点 public void remove() { if (lastReturned == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); // deleted entries are replaced by their successors if (lastReturned.left != null && lastReturned.right != null) /* 若是被删除节点有两个孩子,删除节点e的时候e的引用会被修改成指向原节点的继承者,因此这里先保留next对lastReturned的引用,这样在删除节点后就能获取到继承者的引用,继而继续遍历树 */ next = lastReturned; // 删除节点 deleteEntry(lastReturned); expectedModCount = modCount; lastReturned = null; } }
PrivateEntryIterator类的prevEntry()方法用到了predecessor(Entry<K,V> t)方法,下面对这个方法进行介绍。
predecessor(Entry<K,V> t)方法返回传入节点的“前一个”节点,至于前一个节点是哪一个节点,这和树的遍历次序相关。根据successor(Entry<K,V> t)和predecessor(Entry<K,V> t)方法能够推出TreeMap中树的遍历次序是中根遍历(左孩子-根-右孩子)。
static <K,V> Entry<K,V> predecessor(Entry<K,V> t) { if (t == null) return null; else if (t.left != null) { // 得到左孩子 Entry<K,V> p = t.left; // 对左孩子进行遍历,获取左孩子最右的子孙 while (p.right != null) p = p.right; return p; } else { // 获取t的父节点 Entry<K,V> p = t.parent; Entry<K,V> ch = t; // 沿着右孩子向上查找继承者,直到根节点或找到节点ch是其父节点的右孩子的节点 while (p != null && ch == p.left) { ch = p; p = p.parent; } return p; } }
步骤 | 耗时 | 百分比 |
---|---|---|
需求分析 | 60min | 13.6% |
代码实现 | 300min | 68.1% |
测试 | 60min | 13.6% |
分析总结 | 20min | 4.5% |