課程:《程序設計與數據結構》
班級: 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程序設計與數據結構教程(第二版)》學習指導