課程名稱:《程序設計與數據結構》 學生班級:1723班 學生姓名:唐才銘 學生學號:20172319 實驗教師:王志強老師 課程助教:張師瑜學姐、張之睿學長 實驗時間:2018年11月04日——2018年11月12日 必修/選修:必修
返回目錄html
返回目錄java
返回目錄node
getRight
;contains
;toString
;preorder
;postorder
;getRight
是獲取右子樹,但這裏並無準確說明針對哪一種結點的操做,爲了程序的完整性,便實現了可調用如何結點的右子樹。getRight
具體代碼以下:// 獲取某一結點的右子樹 public String getNodeRightTree(T Elemnet){ String result; BinaryTreeNode node = new BinaryTreeNode(Elemnet); LinkedBinaryTree linkedBinaryTree = new LinkedBinaryTree(); linkedBinaryTree.root = root; if (root==null){ return ""; } else { if (root != null && root.left == null && root.right == null) { linkedBinaryTree.root = root; result = linkedBinaryTree.printTree(); return result; } node = findNode(Elemnet,root); if (node.right!=null){ linkedBinaryTree.root = node.right; result = linkedBinaryTree.printTree(); } else { result = "該結點無右子樹"; } return result; } }
實現結果截圖:
git
contains
判斷樹中是否包含某一元素,這裏使用了原有的find
方法,使得實現更加便捷。contains
具體代碼以下:算法
@Override public boolean contains(T targetElement) { // To be completed as a Programming seatwork T temp; boolean found = false; try { temp = find(targetElement); found = true; } catch (Exception ElementNotFoundExecption){ found = false; } return found; }
find
的具體代碼以下:@Override public T find(T targetElement) throws ElementNotFoundException { BinaryTreeNode<T> current = findNode(targetElement, root); if (current == null) { throw new ElementNotFoundException("LinkedBinaryTree"); } return (current.getElement()); }
實現結果截圖:
express
toString
是輸出樹中元素,本來是直接經過一個遍歷算法來輸出,但爲了實驗的直觀和便於操做,借用了表達式中的printTree
toString
具體代碼:此處用了前序遍從來輸出數組
@Override public String toString() { // To be completed as a Programming seatwork ArrayUnorderedList<T> tempList = new ArrayUnorderedList<T>(); preOrder(root,tempList); return tempList.toString(); }
printTree
具體代碼:public String printTree() { UnorderedListADT<BinaryTreeNode<T>> nodes = new ArrayUnorderedList<BinaryTreeNode<T>>(); UnorderedListADT<Integer> levelList = new ArrayUnorderedList<Integer>(); BinaryTreeNode<T> current; String result = ""; int printDepth = this.getHeight(); 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; }
實現結果截圖:
網絡
preorder
,postorder
,書上只給了inorder
的實現,只需更改遍歷結點的順序便可實現:preorder
與postorder
具體代碼以下:數據結構
private void preOrder(BinaryTreeNode<T> node, ArrayUnorderedList<T> tempList) { // To be completed as a Programming seatwork if (node!=null){ tempList.addToRear(node.element); preOrder(node.left,tempList); preOrder(node.right,tempList); } } private void postOrder(BinaryTreeNode<T> node, ArrayUnorderedList<T> tempList) { // To be completed as a Programming seatwork if (node != null) { postOrder(node.getLeft(), tempList); postOrder(node.getRight(), tempList); tempList.addToRear(node.getElement()); } }
實現結果截圖:
app
具體代碼以下:
// 前序中序構建二叉樹 public BinaryTreeNode BuildTree(char[] preorder, char[] inorder) { return BuildLinkedBinaryTree(preorder, inorder, 0, inorder.length - 1, inorder.length); } /** * @param preorder 前序 * @param inorder 中序 * @param Start 起始位置 * @param End 終止位置 * @param length 結點個數 */ public BinaryTreeNode BuildLinkedBinaryTree(char[] preorder,char[] inorder,int Start, int End,int length) { if (preorder==null||preorder.length == 0 || inorder == null || inorder.length == 0 || length <= 0){ return null; } BinaryTreeNode binaryTreeNode; binaryTreeNode = new BinaryTreeNode(preorder[Start]); if (length==1){ return binaryTreeNode; } int flag=0; while (flag < length){ if (preorder[Start] == inorder[End - flag]){ break; } flag++; } binaryTreeNode.left = BuildLinkedBinaryTree(preorder, inorder,Start + 1, End - flag - 1, length - 1 - flag); binaryTreeNode.right = BuildLinkedBinaryTree(preorder, inorder,Start + length - flag, End, flag ); return binaryTreeNode; }
實現結果截圖:
實驗二 樹-3-決策樹:
實現結果截圖:
實驗二 樹-4-表達式樹
具體代碼以下:
private boolean priority(String[] operator,int size){ // 先對有+ - 的式子進行拆分 boolean found1 = true,found2=true ,found = true; for (int i = 0 ; i< size;i++) { if (operator[i].equals("+")) { found1 = false; } } for (int i = 0 ; i< size;i++) { if (operator[i].equals("-")) { found2 = false; } } if (found1 == false||found2==false){ found = false; } return found; } public BinaryTreeNode Build_Expression_Tree(String[] expression, int size){ // 帶括號的式子暫未實現(遞歸出現的問題太多了(╬ ̄皿 ̄)) BinaryTreeNode binaryTreeNode = new BinaryTreeNode(null); int length = size; // 元素個數 String[] expression_Left_Tree = null; // 左子樹 String[] expression_Right_Tree = null; // 右子樹 for (int i = length - 1; i > 0; i--){ // 遍歷數組元素 String temp = expression[i]; if (temp.equals("+") || temp.equals("-")) { // 若遇到+ - ,則對數組進行此元素左右分割 binaryTreeNode = new BinaryTreeNode(temp); expression_Left_Tree = new String[i]; expression_Right_Tree = new String[length - i - 1]; for (int j = 0; j < expression_Left_Tree.length; j++) { // 拆分結點左邊數組(左子樹) expression_Left_Tree[j] = expression[j]; } for (int k = 0; k < expression_Right_Tree.length; k++) {// 拆分結點右邊數組(右子樹) expression_Right_Tree[k] = expression[i + k + 1]; } if (expression_Left_Tree.length == 1) { // 若結點左子樹數組長度爲1 binaryTreeNode.setLeft(new BinaryTreeNode(expression_Left_Tree[0]));// 輸出數組元素並創建左孩子 if (expression_Right_Tree.length!=1){ // 對該結點右端進行建樹,後面狀況大體同樣不作多餘複述 binaryTreeNode.setRight(Build_Expression_Tree(expression_Right_Tree,expression_Right_Tree.length)); } if (expression_Right_Tree.length==1){ binaryTreeNode.setRight(new BinaryTreeNode(expression_Right_Tree[0])); } return binaryTreeNode; } if (expression_Right_Tree.length == 1) { binaryTreeNode.setRight(new BinaryTreeNode(expression_Right_Tree[0])); if (expression_Left_Tree.length!=1){ binaryTreeNode.setLeft(Build_Expression_Tree(expression_Left_Tree,expression_Left_Tree.length)); } if (expression_Left_Tree.length==1){ binaryTreeNode.setLeft(new BinaryTreeNode(expression_Left_Tree[0])); } return binaryTreeNode; } break; } else if (priority(expression,expression.length)!=false){ // 優先級判斷,此刻數組裏已無加減號 if (temp.equals("*") || temp.equals("/")) { // 若遇到+ - ,則對數組進行此元素左右分割 binaryTreeNode = new BinaryTreeNode(temp); expression_Left_Tree = new String[i]; expression_Right_Tree = new String[length - i - 1]; for (int j = 0; j < expression_Left_Tree.length; j++) { expression_Left_Tree[j] = expression[j]; } for (int k = 0; k < expression_Right_Tree.length; k++) { expression_Right_Tree[k] = expression[i + k + 1]; } if (expression_Left_Tree.length == 1) { binaryTreeNode.setLeft(new BinaryTreeNode(expression_Left_Tree[0])); if (expression_Right_Tree.length!=1){ binaryTreeNode.setRight(Build_Expression_Tree(expression_Right_Tree,expression_Right_Tree.length)); } if (expression_Right_Tree.length==1){ binaryTreeNode.setRight(new BinaryTreeNode(expression_Right_Tree[0])); } return binaryTreeNode; } if (expression_Right_Tree.length == 1) { binaryTreeNode.setRight(new BinaryTreeNode(expression_Right_Tree[0])); if (expression_Left_Tree.length!=1){ binaryTreeNode.setLeft(Build_Expression_Tree(expression_Left_Tree,expression_Left_Tree.length)); } if (expression_Left_Tree.length==1){ binaryTreeNode.setLeft(new BinaryTreeNode(expression_Left_Tree[0])); } return binaryTreeNode; } break; } } } binaryTreeNode.setLeft(Build_Expression_Tree(expression_Left_Tree,expression_Left_Tree.length)); binaryTreeNode.setRight(Build_Expression_Tree(expression_Right_Tree,expression_Right_Tree.length)); return binaryTreeNode; }
removeMin
;findMin
;findMax
操做:@Override public T removeMin() throws EmptyCollectionException { T result = null; if (isEmpty()) { throw new EmptyCollectionException("LinkedBinarySearchTree"); } else { if (root.left == null) { result = root.element; root = root.right; } else { BinaryTreeNode<T> parent = root; BinaryTreeNode<T> current = root.left; while (current.left != null) { parent = current; current = current.left; } result = current.element; parent.left = current.right; } modCount--; } return result; } @Override public T findMin() throws EmptyCollectionException { // To be completed as a Programming Project T result = null; if (isEmpty()) { throw new EmptyCollectionException("LinkedBinarySearchTree"); } else { if (root.left == null) { result = root.element; root = root.right; } else { BinaryTreeNode<T> parent = root; BinaryTreeNode<T> current = root.left; while (current.left != null) { parent = current; current = current.left; } result = current.element; parent.left = current; } } return result; } @Override public T findMax() throws EmptyCollectionException { // To be completed as a Programming Project T result = null; if (isEmpty()) { throw new EmptyCollectionException("LinkedBinarySearchTree"); } else { if (root.right== null) { result = root.element; root = root.left; } else { BinaryTreeNode<T> parent = root; BinaryTreeNode<T> current = root.right; while (current.right != null) { parent = current; current = current.right; } result = current.element; parent.right = current; } } return result; }
實現結果截圖:
TreeMap
:TreeMap 是一個有序的key-value集合,它是經過紅黑樹實現的。
TreeMap 繼承於AbstractMap,因此它是一個Map,即一個key-value集合。
TreeMap 實現了NavigableMap接口,意味着它支持一系列的導航方法。好比返回有序的key集合。
TreeMap 實現了Cloneable接口,意味着它能被克隆。
TreeMap 實現了java.io.Serializable接口,意味着它支持序列化。
TreeMap基於紅黑樹(Red-Black tree)實現。該映射根據其鍵的天然順序進行排序,或者根據建立映射時提供的 Comparator 進行排序,具體取決於使用的構造方法。
TreeMap的基本操做 containsKey、get、put 和 remove 的時間複雜度是 log(n) 。
另外,TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fastl的。
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable { // 比較器對象 private final Comparator<? super K> comparator; // 根節點 private transient Entry<K,V> root; // 集合大小 private transient int size = 0; // 樹結構被修改的次數 private transient int modCount = 0; // 靜態內部類用來表示節點類型 static final class Entry<K,V> implements Map.Entry<K,V> { K key; // 鍵 V value; // 值 Entry<K,V> left; // 指向左子樹的引用(指針) Entry<K,V> right; // 指向右子樹的引用(指針) Entry<K,V> parent; // 指向父節點的引用(指針) boolean color = BLACK; // } }
public TreeMap() { // 1,無參構造方法 comparator = null; // 默認比較機制 } public TreeMap(Comparator<? super K> comparator) { // 2,自定義比較器的構造方法 this.comparator = comparator; } public TreeMap(Map<? extends K, ? extends V> m) { // 3,構造已知Map對象爲TreeMap comparator = null; // 默認比較機制 putAll(m); } public TreeMap(SortedMap<K, ? extends V> m) { // 4,構造已知的SortedMap對象爲TreeMap comparator = m.comparator(); // 使用已知對象的構造器 try { buildFromSorted(m.size(), m.entrySet().iterator(), null, null); } catch (java.io.IOException cannotHappen) { } catch (ClassNotFoundException cannotHappen) { } }
// 紅黑樹的節點顏色--紅色 private static final boolean RED = false; // 紅黑樹的節點顏色--黑色 private static final boolean BLACK = true; // 「紅黑樹的節點」對應的類。 // 包含了 key(鍵)、value(值)、left(左孩子)、right(右孩子)、parent(父節點)、color(顏色) static final class Entry<K,V> implements Map.Entry<K,V> { // 鍵 K key; // 值 V value; // 左孩子 Entry<K,V> left = null; // 右孩子 Entry<K,V> right = null; // 父節點 Entry<K,V> parent; // 當前節點顏色 boolean color = BLACK; // 構造函數 Entry(K key, V value, Entry<K,V> parent) { this.key = key; this.value = value; this.parent = parent; } // 返回「鍵」 public K getKey() { return key; } // 返回「值」 public V getValue() { return value; } // 更新「值」,返回舊的值 public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } // 判斷兩個節點是否相等的函數,覆蓋equals()函數。 // 若兩個節點的「key相等」而且「value相等」,則兩個節點相等 public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>)o; return valEquals(key,e.getKey()) && valEquals(value,e.getValue()); } // 覆蓋hashCode函數。 public int hashCode() { int keyHash = (key==null ? 0 : key.hashCode()); int valueHash = (value==null ? 0 : value.hashCode()); return keyHash ^ valueHash; } // 覆蓋toString()函數。 public String toString() { return key + "=" + value; } }
// 返回「紅黑樹的第一個節點」 final Entry<K,V> getFirstEntry() { Entry<K,V> p = root; if (p != null) while (p.left != null) p = p.left; return p; } // 返回「紅黑樹的最後一個節點」 final Entry<K,V> getLastEntry() { Entry<K,V> p = root; if (p != null) while (p.right != null) p = p.right; return p; } // 返回「節點t的後繼節點」 static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) { if (t == null) return null; else if (t.right != null) { Entry<K,V> p = t.right; while (p.left != null) p = p.left; return p; } else { Entry<K,V> p = t.parent; Entry<K,V> ch = t; while (p != null && ch == p.right) { ch = p; p = p.parent; } return p; } } // 返回「節點t的前繼節點」 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 { Entry<K,V> p = t.parent; Entry<K,V> ch = t; while (p != null && ch == p.left) { ch = p; p = p.parent; } return p; } } // 返回「節點p的顏色」 // 根據「紅黑樹的特性」可知:空節點顏色是黑色。 private static <K,V> boolean colorOf(Entry<K,V> p) { return (p == null ? BLACK : p.color); } // 返回「節點p的父節點」 private static <K,V> Entry<K,V> parentOf(Entry<K,V> p) { return (p == null ? null: p.parent); } // 設置「節點p的顏色爲c」 private static <K,V> void setColor(Entry<K,V> p, boolean c) { if (p != null) p.color = c; } // 設置「節點p的左孩子」 private static <K,V> Entry<K,V> leftOf(Entry<K,V> p) { return (p == null) ? null: p.left; } // 設置「節點p的右孩子」 private static <K,V> Entry<K,V> rightOf(Entry<K,V> p) { return (p == null) ? null: p.right; }
// 對節點p執行「左旋」操做 private void rotateLeft(Entry<K,V> p) { if (p != null) { Entry<K,V> r = p.right; p.right = r.left; if (r.left != null) r.left.parent = p; r.parent = p.parent; if (p.parent == null) root = r; else if (p.parent.left == p) p.parent.left = r; else p.parent.right = r; r.left = p; p.parent = r; } } // 對節點p執行「右旋」操做 private void rotateRight(Entry<K,V> p) { if (p != null) { Entry<K,V> l = p.left; p.left = l.right; if (l.right != null) l.right.parent = p; l.parent = p.parent; if (p.parent == null) root = l; else if (p.parent.right == p) p.parent.right = l; else p.parent.left = l; l.right = p; p.parent = l; } }
// 插入以後的修正操做。 // 目的是保證:紅黑樹插入節點以後,仍然是一顆紅黑樹 private void fixAfterInsertion(Entry<K,V> x) { x.color = RED; while (x != null && x != root && x.parent.color == RED) { if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { Entry<K,V> y = rightOf(parentOf(parentOf(x))); if (colorOf(y) == RED) { setColor(parentOf(x), BLACK); setColor(y, BLACK); setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); } else { if (x == rightOf(parentOf(x))) { x = parentOf(x); rotateLeft(x); } setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateRight(parentOf(parentOf(x))); } } else { Entry<K,V> y = leftOf(parentOf(parentOf(x))); if (colorOf(y) == RED) { setColor(parentOf(x), BLACK); setColor(y, BLACK); setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); } else { if (x == leftOf(parentOf(x))) { x = parentOf(x); rotateRight(x); } setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateLeft(parentOf(parentOf(x))); } } } root.color = BLACK; } // 刪除「紅黑樹的節點p」 private void deleteEntry(Entry<K,V> p) { modCount++; size--; // If strictly internal, copy successor's element to p and then make p // point to successor. if (p.left != null && p.right != null) { Entry<K,V> s = successor (p); p.key = s.key; p.value = s.value; p = s; } // p has 2 children // Start fixup at replacement node, if it exists. Entry<K,V> replacement = (p.left != null ? p.left : p.right); if (replacement != null) { // Link replacement to parent replacement.parent = p.parent; if (p.parent == null) root = replacement; else if (p == p.parent.left) p.parent.left = replacement; else p.parent.right = replacement; // Null out links so they are OK to use by fixAfterDeletion. p.left = p.right = p.parent = null; // Fix replacement if (p.color == BLACK) fixAfterDeletion(replacement); } else if (p.parent == null) { // return if we are the only node. root = null; } else { // No children. Use self as phantom replacement and unlink. if (p.color == BLACK) fixAfterDeletion(p); if (p.parent != null) { if (p == p.parent.left) p.parent.left = null; else if (p == p.parent.right) p.parent.right = null; p.parent = null; } } } // 刪除以後的修正操做。 // 目的是保證:紅黑樹刪除節點以後,仍然是一顆紅黑樹 private void fixAfterDeletion(Entry<K,V> x) { while (x != root && colorOf(x) == BLACK) { if (x == leftOf(parentOf(x))) { Entry<K,V> sib = rightOf(parentOf(x)); if (colorOf(sib) == RED) { setColor(sib, BLACK); setColor(parentOf(x), RED); rotateLeft(parentOf(x)); sib = rightOf(parentOf(x)); } if (colorOf(leftOf(sib)) == BLACK && colorOf(rightOf(sib)) == BLACK) { setColor(sib, RED); x = parentOf(x); } else { if (colorOf(rightOf(sib)) == BLACK) { setColor(leftOf(sib), BLACK); setColor(sib, RED); rotateRight(sib); sib = rightOf(parentOf(x)); } setColor(sib, colorOf(parentOf(x))); setColor(parentOf(x), BLACK); setColor(rightOf(sib), BLACK); rotateLeft(parentOf(x)); x = root; } } else { // symmetric Entry<K,V> sib = leftOf(parentOf(x)); if (colorOf(sib) == RED) { setColor(sib, BLACK); setColor(parentOf(x), RED); rotateRight(parentOf(x)); sib = leftOf(parentOf(x)); } if (colorOf(rightOf(sib)) == BLACK && colorOf(leftOf(sib)) == BLACK) { setColor(sib, RED); x = parentOf(x); } else { if (colorOf(leftOf(sib)) == BLACK) { setColor(rightOf(sib), BLACK); setColor(sib, RED); rotateLeft(sib); sib = leftOf(parentOf(x)); } setColor(sib, colorOf(parentOf(x))); setColor(parentOf(x), BLACK); setColor(leftOf(sib), BLACK); rotateRight(parentOf(x)); x = root; } } } setColor(x, BLACK); }
一、節點是紅色或黑色
二、根節點是黑色
三、全部的葉子(NIL空節點)是黑色的
四、每一個紅色節點的兩個兒子均爲黑色,即不可能有連續的兩個紅色節點
五、從任一節點到其葉子(NIL空節點)的路徑都包含相同數目的黑節點
put
方法
// 將「key, value」添加到TreeMap中 // 理解TreeMap的前提是掌握「紅黑樹」。 // 若理解「紅黑樹中添加節點」的算法,則很容易理解put。 public V put(K key, V value) { Entry<K,V> t = root; // 若紅黑樹爲空,則插入根節點 if (t == null) { // TBD: // 5045147: (coll) Adding null to an empty TreeSet should // throw NullPointerException // // compare(key, key); // type check root = new Entry<K,V>(key, value, null); size = 1; modCount++; return null; } int cmp; Entry<K,V> parent; // split comparator and comparable paths Comparator<? super K> cpr = comparator; // 在二叉樹(紅黑樹是特殊的二叉樹)中,找到(key, value)的插入位置。 // 紅黑樹是以key來進行排序的,因此這裏以key來進行查找。 if (cpr != null) { do { parent = t; cmp = cpr.compare(key, t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } else { if (key == null) throw new NullPointerException(); Comparable<? super K> k = (Comparable<? super K>) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } // 新建紅黑樹的節點(e) Entry<K,V> e = new Entry<K,V>(key, value, parent); if (cmp < 0) parent.left = e; else parent.right = e; // 紅黑樹插入節點後,再也不是一顆紅黑樹; // 這裏經過fixAfterInsertion的處理,來恢復紅黑樹的特性。 fixAfterInsertion(e); size++; modCount++; return null; }
1.校驗根節點:校驗根節點是否爲空,若爲空則根據傳入的key-value的值建立一個新的節點,若根節點不爲空則繼續第二步
2.尋找插入位置:因爲TreeMap內部是紅黑樹實現的,在插入元素時,遍歷左子樹,或者右子樹
3.新建並恢復:在第二步中其實是須要肯定當前插入節點的位置,而這一步是實際的插入操做,而插入以後爲啥還須要調用fixAfterInsertion方法,紅黑樹插入一個節點後可能會破壞紅黑樹的性質,所以須要使紅黑樹重新達到平衡,
HashMap
:TreeNode
: HashMap的靜態內部類,繼承與LinkedHashMap.Entry<K,V>類,真正維護紅黑樹結構的方法都在其內部。static final class TreeNode<K, V> extends LinkedHashMap.Entry<K, V> { TreeNode<K, V> parent; // red-black tree links TreeNode<K, V> left; TreeNode<K, V> right; TreeNode<K, V> prev; // needed to unlink next upon deletion boolean red; TreeNode(int hash, K key, V val, Node<K, V> next) { super(hash, key, val, next); } final void treeify(Node<K,V>[] tab) { // ...... } static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root, TreeNode<K,V> x) { // ...... } static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root, TreeNode<K,V> p) { // ...... } static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root, TreeNode<K,V> p) { // ...... } // ......其他方法省略 }
treeifyBin
:在HashMap中put方法時候,但數組中某個位置的鏈表長度大於某一值時,會調用treeifyBin方法將鏈表轉化爲紅黑樹。final void treeifyBin(Node<K, V>[] tab, int hash) { int n, index; Node<K, V> e; if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) // resize()方法這裏不過多介紹,感興趣的能夠去看上面的連接。 resize(); // 經過hash求出bucket的位置。 else if ((e = tab[index = (n - 1) & hash]) != null) { TreeNode<K, V> hd = null, tl = null; do { // 將每一個節點包裝成TreeNode。 TreeNode<K, V> p = replacementTreeNode(e, null); if (tl == null) hd = p; else { // 將全部TreeNode鏈接在一塊兒此時只是鏈表結構。 p.prev = tl; tl.next = p; } tl = p; } while ((e = e.next) != null); if ((tab[index] = hd) != null) // 對TreeNode鏈表進行樹化。 hd.treeify(tab); } }
treeify
:將Treenode鏈轉化成紅黑樹,第一次循環會將鏈表中的首節點做爲紅黑樹的根,然後的循環會將鏈表中的的項經過比較hash值而後鏈接到相應樹節點的左邊或者右邊,插入可能會破壞樹的結構。final void treeify(Node<K, V>[] tab) { TreeNode<K, V> root = null; // 以for循環的方式遍歷剛纔咱們建立的鏈表。 for (TreeNode<K, V> x = this, next; x != null; x = next) { // next向前推動。 next = (TreeNode<K, V>) x.next; x.left = x.right = null; // 爲樹根節點賦值。 if (root == null) { x.parent = null; x.red = false; root = x; } else { // x即爲當前訪問鏈表中的項。 K k = x.key; int h = x.hash; Class<?> kc = null; // 此時紅黑樹已經有了根節點,上面獲取了當前加入紅黑樹的項的key和hash值進入核心循環。 // 這裏從root開始,是以一個自頂向下的方式遍歷添加。 // for循環沒有控制條件,由代碼內break跳出循環。 for (TreeNode<K, V> p = root;;) { // dir:directory,比較添加項與當前樹中訪問節點的hash值判斷加入項的路徑,-1爲左子樹,+1爲右子樹。 // ph:parent hash。 int dir, ph; K pk = p.key; if ((ph = p.hash) > h) dir = -1; else if (ph < h) dir = 1; else if ((kc == null && (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) dir = tieBreakOrder(k, pk); // xp:x parent。 TreeNode<K, V> xp = p; // 找到符合x添加條件的節點。 if ((p = (dir <= 0) ? p.left : p.right) == null) { x.parent = xp; // 若是xp的hash值大於x的hash值,將x添加在xp的左邊。 if (dir <= 0) xp.left = x; // 反之添加在xp的右邊。 else xp.right = x; // 維護添加後紅黑樹的紅黑結構。 root = balanceInsertion(root, x); // 跳出循環當前鏈表中的項成功的添加到了紅黑樹中。 break; } } } } // Ensures that the given root is the first node of its bin,本身翻譯一下。 moveRootToFront(tab, root); }
balanceInsertion
: 從新平衡二叉樹static <K, V> TreeNode<K, V> balanceInsertion(TreeNode<K, V> root, TreeNode<K, V> x) { // 正如開頭所說,新加入樹節點默認都是紅色的,不會破壞樹的結構。 x.red = true; // 這些變量名不是做者隨便定義的都是有意義的。 // xp:x parent,表明x的父節點。 // xpp:x parent parent,表明x的祖父節點 // xppl:x parent parent left,表明x的祖父的左節點。 // xppr:x parent parent right,表明x的祖父的右節點。 for (TreeNode<K, V> xp, xpp, xppl, xppr;;) { // 若是x的父節點爲null說明只有一個節點,該節點爲根節點,根節點爲黑色,red = false。 if ((xp = x.parent) == null) { x.red = false; return x; } // 進入else說明不是根節點。 // 若是父節點是黑色,那麼大吉大利(今晚吃雞),紅色的x節點能夠直接添加到黑色節點後面,返回根就好了不須要任何多餘的操做。 // 若是父節點是紅色的,但祖父節點爲空的話也能夠直接返回根此時父節點就是根節點,由於根必須是黑色的,添加在後面沒有任何問題。 else if (!xp.red || (xpp = xp.parent) == null) return root; // 一旦咱們進入到這裏就說明了兩件是情 // 1.x的父節點xp是紅色的,這樣就遇到兩個紅色節點相連的問題,因此必須通過旋轉變換。 // 2.x的祖父節點xpp不爲空。 // 判斷若是父節點是不是祖父節點的左節點 if (xp == (xppl = xpp.left)) { // 父節點xp是祖父的左節點xppr // 判斷祖父節點的右節點不爲空而且是不是紅色的 // 此時xpp的左右節點都是紅的,因此直接進行上面所說的第三種變換,將兩個子節點變成黑色,將xpp變成紅色,而後將紅色節點x順利的添加到了xp的後面。 // 這裏你們有疑問爲何將x = xpp? // 這是因爲將xpp變成紅色之後可能與xpp的父節點發生兩個相連紅色節點的衝突,這就又構成了第二種旋轉變換,因此必須從底向上的進行變換,直到根。 // 因此令x = xpp,而後進行下下一層循環,接着往上走。 if ((xppr = xpp.right) != null && xppr.red) { xppr.red = false; xp.red = false; xpp.red = true; x = xpp; } // 進入到這個else裏面說明。 // 父節點xp是祖父的左節點xppr。 // 祖父節點xpp的右節點xppr是黑色節點或者爲空,默認規定空節點也是黑色的。 // 下面要判斷x是xp的左節點仍是右節點。 else { // x是xp的右節點,此時的結構是:xpp左->xp右->x。這明顯是第二中變換須要進行兩次旋轉,這裏先進行一次旋轉。 // 下面是第一次旋轉。 if (x == xp.right) { root = rotateLeft(root, x = xp); xpp = (xp = x.parent) == null ? null : xp.parent; } // 針對自己就是xpp左->xp左->x的結構或者因爲上面的旋轉形成的這種結構進行一次旋轉。 if (xp != null) { xp.red = false; if (xpp != null) { xpp.red = true; root = rotateRight(root, xpp); } } } } // 這裏的分析方式和前面的相對稱只不過所有在右測再也不重複分析。 else { if (xppl != null && xppl.red) { xppl.red = false; xp.red = false; xpp.red = true; x = xpp; } else { if (x == xp.left) { root = rotateRight(root, x = xp); xpp = (xp = x.parent) == null ? null : xp.parent; } if (xp != null) { xp.red = false; if (xpp != null) { xpp.red = true; root = rotateLeft(root, xpp); } } } } } }