列表的一些常見操做
html
有序列表的特有操做
java
若是二叉查找樹不平衡,其效率可能比線性結構的還要低。例如蛻化樹看起來更像一個鏈表,事實上它的效率比鏈表的還低,由於每一個結點附帶有額外的開銷。
node
咱們的目標是保持樹的最大路徑長度爲(或接近)log2n。git
樹(或樹的任何子樹)只有兩種途徑變得不平衡:插入結點或刪除結點。所以在每次進行這兩種操做時,都必須更新平衡因子,而後從插入或刪除結點的那個地方開始檢查樹的平衡性。上溯到根結點,因此AVL樹一般最好實現爲每一個結點都包含一個指向父結點的引用。函數
紅黑樹示意:
學習
public T removeElement(T targetElement) { T result = null; if (isEmpty()) {//樹爲空時拋出異常 throw new ElementNotFoundException("LinkedbinarySearchTree"); } else {//樹不爲空 BinaryTreeNode<T> parent = null; if (((Comparable<T>) targetElement).equals(root.getElement())) {//要刪除的元素是根結點 result = root.element; BinaryTreeNode<T> temp = replacement(root); if (temp == null) {//找不到結點替換 root = null; } else { //用找到的結點替換根結點 root.element = temp.element; root.setLeft(temp.getLeft()); root.setRight(temp.getRight()); } modCount--; } else {//要刪除根節點的孩子 parent = root; if (((Comparable<T>) targetElement) .compareTo(root.getElement()) < 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) {//用來刪除除根之外的目標元素 T result = null; if (node == null) { throw new ElementNotFoundException("LinkedbinarySearchTree"); } else { if (((Comparable<T>) targetElement).equals(node.getElement())) {//找到目標元素 result = node.element; BinaryTreeNode<T> temp = replacement(node);//將node元素刪除 //往下繼續查找目標元素,看看左右孩子是不是 if (parent.right == node) { parent.right = temp; } else { parent.left = temp; } modCount--; } else {//若是目標元素比根結點小,則在根結點左側,再次使用該方法從左子樹中查找目標元素 parent = node; if (((Comparable<T>) targetElement) .compareTo(root.getElement()) < 0) { result = removeElement(targetElement, root.getLeft(), parent); } else {//目標元素比根結點大,再次使用該方法從右子樹中查找目標元素 result = removeElement(targetElement, root.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; }
問題三解決:在課堂上,老師提到了前驅結點,就是對一棵樹進行中序排序,造成一個序列,書上所提到的返回中序後繼者的意思就是排序後的序列的被刪除結點的前驅結點或後驅結點均可以,由本身來定義。例如
測試
對這棵樹進行中序排序爲:2 3 4 5 6。刪除結點3後,能夠返回前驅結點2,也能夠返回後驅結點4。.net
問題一:對於AVL樹的實現,一開始想的是在printTree裏面添加,在輸出樹以前對樹進行平衡,而後測試類裏直接定義一個完整的樹,可是這樣作的話考慮到樹的不少狀況,
tree = new AVLTreeNode<T>(element, (AVLTree<T>) null, null);
若是傳入結點爲空須要建立一個結點,若是要添加的元素比根結點小tree.left = addElement(tree.left, element);
表示將其添加到結點左邊,並當(height(tree.right) - height(tree.left) == -2)
時,說明左子樹過長,這時若是(element.compareTo(tree.left.getElement()) < 0
則說明元素比左孩子小,這樣應該使用tree.rightRightRotation(tree)
方法,進行右旋,不然的話進行右左旋。
但這時問題出現了,運行顯示Exception in thread "main" java.lang.NullPointerException錯誤,也就是空指針,在左旋方法中
指針
將代碼更改成若是(element.compareTo(tree.left.getElement()) < 0
,進行左旋tree = leftLeftRotation(tree);
不然進行左右旋tree = leftRightRotation(tree);
,這是由於若是添加到左孩子的右孩子位置,符合「對於由樹根左孩子的右子樹中較長路徑而致使的不平衡,須要先讓樹根左孩子的右孩子繞其父結點進行一次左旋,再讓樹根的左孩子繞樹根進行一次右旋。」的定義,參考左右旋的圖示,符合要求,若是用右左旋的話,左子樹的左孩子是空的,並不能將她右旋,因此出現空指針錯誤。調試
無
這章內容關於AVL樹和紅黑樹的描述很難理解,狀況太多了,須要進行分類整理並結合圖示,AVL須要主要弄清楚旋轉狀況,紅黑樹要時刻牢記向三個性質轉變。
代碼行數(新增/累積) | 博客量(新增/累積) | 學習時間(新增/累積) | |
---|---|---|---|
目標 | 5000行 | 30篇 | 400小時 |
第一週 | 0/0 | 1/1 | 8/8 |
第二週 | 1163/1163 | 1/2 | 15/23 |
第三週 | 774/1937 | 1/3 | 12/50 |
第四周 | 3596/5569 | 2/5 | 12/62 |
第五週 | 3329/8898 | 2/7 | 12/74 |
第六週 | 4541/13439 | 3/10 | 12/86 |