2.二叉查找樹不只具備二叉樹的操做,還具備如下的特殊操做:html
public LinkedBinarySearchTree() { super(); } public LinkedBinarySearchTree(T element) { super(element); if (!(element instanceof Comparable)) throw new NonComparableElementException("LinkedBinarySearchTree"); }
(4)若是這個新元素大於或等於樹根處存儲的那個元素且根的右孩子不是null,則會遍歷根的右孩子,並再次進行比較操做。java
public void addElement(T element) { if (!(element instanceof Comparable)) throw new NonComparableElementException("LinkedBinarySearchTree"); Comparable<T> comparableElement = (Comparable<T>)element; if (isEmpty()) root = new BinaryTreeNode<T>(element); else { if (comparableElement.compareTo(root.getElement()) < 0) { if (root.getLeft() == null) this.getRootNode().setLeft(new BinaryTreeNode<T>(element)); else addElement(element, root.getLeft()); } else { if (root.getRight() == null) this.getRootNode().setRight(new BinaryTreeNode<T>(element)); else addElement(element, root.getRight()); } } modCount++; } private void addElement(T element, BinaryTreeNode<T> node) { Comparable<T> comparableElement = (Comparable<T>)element; if (comparableElement.compareTo(node.getElement()) < 0) { if (node.getLeft() == null) node.setLeft(new BinaryTreeNode<T>(element)); else addElement(element, node.getLeft()); } else { if (node.getRight() == null) node.setRight(new BinaryTreeNode<T>(element)); else addElement(element, node.getRight()); } }
public T removeElement(T targetElement) throws ElementNotFoundException { T result = null; if (isEmpty()) throw new ElementNotFoundException("LinkedBinarySearchTree"); else { BinaryTreeNode<T> parent = null; if (((Comparable<T>)targetElement).equals(root.element)) { result = root.element; BinaryTreeNode<T> temp = replacement(root); if (temp == null) root = null; else { root.element = temp.element; root.setRight(temp.right); root.setLeft(temp.left); } modCount--; } else { parent = root; if (((Comparable)targetElement).compareTo(root.element) < 0) result = removeElement(targetElement, root.getLeft(), parent); else result = removeElement(targetElement, root.getRight(), parent); } } return result; } private T removeElement(T targetElement, BinaryTreeNode<T> node, BinaryTreeNode<T> parent) throws ElementNotFoundException { T result = null; if (node == null) throw new ElementNotFoundException("LinkedBinarySearchTree"); else { if (((Comparable<T>)targetElement).equals(node.element)) { result = node.element; BinaryTreeNode<T> temp = replacement(node); if (parent.right == node) parent.right = temp; else parent.left = temp; modCount--; } else { parent = node; if (((Comparable)targetElement).compareTo(node.element) < 0) result = removeElement(targetElement, node.getLeft(), parent); else result = removeElement(targetElement, node.getRight(), parent); } } return result; } private BinaryTreeNode<T> replacement(BinaryTreeNode<T> node) { BinaryTreeNode<T> result = null; if ((node.left == null) && (node.right == null)) result = null; else if ((node.left != null) && (node.right == null)) result = node.left; else if ((node.left == null) && (node.right != null)) result = node.right; else { BinaryTreeNode<T> current = node.right; BinaryTreeNode<T> parent = node; while (current.left != null) { parent = current; current = current.left; } current.left = node.left; if (node.right != current) { parent.left = current.right; current.right = node.right; } result = current; } return result; }
public void removeAllOccurrences(T targetElement) throws ElementNotFoundException { removeElement(targetElement); try { while (contains((T)targetElement)) removeElement(targetElement); } catch (Exception ElementNotFoundException) { } }
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; }
17.AVL樹的右旋:由下圖可知咱們是在結點T的左結點的左子樹上作了插入元素的操做,咱們稱這種狀況爲左左狀況,咱們應該進行右旋轉(只需旋轉一次,故是單旋轉)【步驟與右旋步驟同樣】node
過程:git
18.AVL樹的左旋:由下圖可知咱們是在結點T的右結點的右子樹上作了插入元素的操做,咱們稱這種狀況爲右右狀況,咱們應該進行左旋轉(只需旋轉一次,故是單旋轉)【步驟與左旋步驟同樣】數據結構
過程:函數
19.AVL樹的左右(先左後右)旋:以下圖,只是單純的進行一次旋轉,獲得的樹仍然是不平衡的。因此應該進行二次旋轉。學習
20.AVL樹的右左(先右後左)旋:以下圖,只是單純的進行一次旋轉,獲得的樹仍然是不平衡的。因此應該進行二次旋轉。this
22.紅黑樹的元素添加及刪除。.net
public Node getSuccessor(Node delNode){ //參數爲被刪除的節點 //定義一個當前節點的引用,直接讓往下走一步,走到被刪除節點的右節點 Node curr=delNode.right; Node successor=curr; //用來指向中級後續節點 Node sucParent=null; //用來指向中級後續節點的父節點 while(curr!=null){ sucParent=successor; successor=curr; curr=curr.left; } //循環中止,中級後續節點被找出 if(successor!=delNode.right){ //將後繼節點的子節點(只可能有右節點)替補到後繼節點的位置上 sucParent.left=successor.right; //將被刪除的右孩子鏈接到後繼節點的右節點上 successor.right=delNode.right; //將被刪除的左孩子鏈接到後繼節點的右節點上(就是用後繼節點替換掉被刪除的節點) } return successor; }
問題引伸解決方案:若是直接刪結點,整個樹的大小順序就亂了,因此須要考慮,在樹中找到一個合適的節點來把這個結點給替換掉,用這種方法來保持整個樹的穩定。須要在樹中找出全部比被刪除節點的值大的全部數,並在這些數中找出一個最小的數來。設計
問題2解決方案:刪除時樹的平衡性受到破壞,提升它的操做的時間複雜度。而AVL樹就不會出現這種狀況,樹的高度始終是O(lgN).高度越小,對樹的一些基本操做的時間複雜度就會越小。
private void insert(RBTNode<T> node) { int cmp; RBTNode<T> y = null; RBTNode<T> x = this.mRoot; // 1. 將紅黑樹看成一顆二叉查找樹,將節點添加到二叉查找樹中。 while (x != null) { y = x; cmp = node.key.compareTo(x.key); if (cmp < 0) x = x.left; else x = x.right; } node.parent = y; if (y!=null) { cmp = node.key.compareTo(y.key); if (cmp < 0) y.left = node; else y.right = node; } else { this.mRoot = node; } // 2. 設置節點的顏色爲紅色 node.color = RED; // 3. 將它從新修正爲一顆二叉查找樹 insertFixUp(node); } /* * 新建結點(key),並將其插入到紅黑樹中 * * 參數說明: * key 插入結點的鍵值 */ public void insert(T key) { RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null); // 若是新建結點失敗,則返回。 if (node != null) insert(node); }
/* * 刪除結點(node),並返回被刪除的結點 * * 參數說明: * node 刪除的結點 */ private void remove(RBTNode<T> node) { RBTNode<T> child, parent; boolean color; // 被刪除節點的"左右孩子都不爲空"的狀況。 if ( (node.left!=null) && (node.right!=null) ) { // 被刪節點的後繼節點。(稱爲"取代節點") // 用它來取代"被刪節點"的位置,而後再將"被刪節點"去掉。 RBTNode<T> replace = node; // 獲取後繼節點 replace = replace.right; while (replace.left != null) replace = replace.left; // "node節點"不是根節點(只有根節點不存在父節點) if (parentOf(node)!=null) { if (parentOf(node).left == node) parentOf(node).left = replace; else parentOf(node).right = replace; } else { // "node節點"是根節點,更新根節點。 this.mRoot = replace; } // child是"取代節點"的右孩子,也是須要"調整的節點"。 // "取代節點"確定不存在左孩子!由於它是一個後繼節點。 child = replace.right; parent = parentOf(replace); // 保存"取代節點"的顏色 color = colorOf(replace); // "被刪除節點"是"它的後繼節點的父節點" if (parent == node) { parent = replace; } else { // child不爲空 if (child!=null) setParent(child, parent); parent.left = child; replace.right = node.right; setParent(node.right, replace); } replace.parent = node.parent; replace.color = node.color; replace.left = node.left; node.left.parent = replace; if (color == BLACK) removeFixUp(child, parent); node = null; return ; } if (node.left !=null) { child = node.left; } else { child = node.right; } parent = node.parent; // 保存"取代節點"的顏色 color = node.color; if (child!=null) child.parent = parent; // "node節點"不是根節點 if (parent!=null) { if (parent.left == node) parent.left = child; else parent.right = child; } else { this.mRoot = child; } if (color == BLACK) removeFixUp(child, parent); node = null; } /* * 刪除結點(z),並返回被刪除的結點 * * 參數說明: * tree 紅黑樹的根結點 * z 刪除的結點 */ public void remove(T key) { RBTNode<T> node; if ((node = search(mRoot, key)) != null) remove(node); }
問題3:按着書上講的左旋右旋步驟來作,會出現錯誤
問題代碼的過程爲下圖,很顯然出現了覆蓋結點,致使丟失結點的問題。
改正:
過程:
代碼行數(新增/累積) | 博客量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
---|---|---|---|---|
目標 | 5000行 | 30篇 | 400小時 | |
第一週 | 0/0 | 1/1 | 2/2 | |
第二週 | 1010/1010 | 1/2 | 10/12 | |
第三週 | 651/1661 | 1/3 | 13/25 | |
第四周 | 2205/3866 | 1/4 | 15/40 | |
第五週 | 967/4833 | 2/6 | 22/62 | |
第六週 | 1680/6513 | 1/7 | 34/96 | |
第七週 | 2196/8709 | 1/8 | 35/131 |
計劃學習時間:30小時
實際學習時間:35小時
改進狀況:AVL樹和紅黑樹真的耗費了大量的時間!