課程:程序設計與數據結構
班級: 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% |