1.從新平衡2子樹,體現了概括思想,以及簡單的建模思想。java
2.旋轉名字不太好理解。本身以爲 「沿襲」 更恰當。本身爲了進行沿襲這個動做。概括了2個定理:node
1.父節點可垂直變爲其左孩子或者左孩子的左孩子。左右同理。函數
2.子樹內部任意一支子樹可代替原子樹。測試
有這2個定理。就能夠不用理解書上的旋轉了,用本身的沿襲就能夠完成單旋轉和雙旋轉。畢竟大部分書,都只是告訴你如何旋轉,還不如本身建模,抽象,定義定理。來實現。spa
3.從新計算樹高度時,從變化的葉子開始要一直往父節點從新計算,因此很適合使用遞歸的方法,並保障從上往下時,是逐層往下,那麼就能夠保證插入和刪除之後,會逐層檢查子樹高度,以及對比是否須要旋轉日誌
而旋轉後須要跟新子樹的父節點。因此參考書上的方法是有返回節點的,一邊遞歸回去的時候更新給父節點,而本身爲了方便裏面,沒喲使用返回值,而是多加參數的方法,不簡便,但好理解。code
概括和抽象建模思想體如今哪裏。第一步到第二步的轉變側重體現了概括,抽象,建模等基本思想。 第二步到第三步更體現了爲了解決實際問題,修改模型的能力。val是一個進行抽象思惟的練習的好例子,很是值得複習blog
採用了2中不一樣的函數來完成 add .一個帶返回值,一個不帶返回值,而是使用參數。遞歸
package com.linson.datastrcture; //本身的插入在遞歸中,並無和書上返回節點。本身感受本身無返回值的更好理解,代替方案就是放入一個頭節點的父節點。好理解,但也繁瑣點。 //遞歸返回根節點從代碼的簡潔上絕對更優,只是帶參數的方法更容易理解。 //avl這個例子很值得複習。1.遞歸時的問題模型的肯定 2.平衡樹時的建模思惟。 3.樹高的遞歸計算,遞歸影響。這個很不錯。4.compareble的系統庫接口使用。5.左小右大思路的利用。 //6,刪除時,須要平衡的時候會存在高的那一邊的左右子樹又同樣高。比父節點的兄弟都高2級。 public class MyAVL<T extends Comparable<T>> { public static class MyAVLNode<E extends Comparable<E>> { public E mElement; public MyAVLNode<E> mLeftNode; public MyAVLNode<E> mRightNode; public int mHeight; public MyAVLNode(E _value,MyAVLNode<E> left,MyAVLNode<E> right) { mElement=_value; mLeftNode=left; mRightNode=right; mHeight=1; } public int compareTo(E other) { return mElement.compareTo(other); } } public MyAVL() { mRootNode=null; } //問題:插入節點到樹。組合:插入到節點,插入到左樹,插入到右樹. 基本值節點爲空。能夠插入。不須要再判斷是否繼續插入左或者右 //節點高度默認是1,添加節點,必須逐層檢測父節點:左右子樹中最大值+1誰否大於現值? 大於要往上再檢查,一直到某上層沒變化。 //因此返回值能夠改成返回是否須要檢查高度。可是想一下,又要增長返回值,又要判斷是否須要檢查,還不如每層都檢查,反正AVL的話,數據再大也不會很高。1024才11層。 public void add(T element,MyAVLNode<T> subTreeNode,MyAVLNode<T> fatherNode,boolean isLeft) { if(subTreeNode==null) { MyAVLNode<T> TempNode=new MyAVLNode<T>(element, null, null);//節點和樹的泛型都實現了對比接口。因此樹的參數,能夠直接放入到節點中 if(fatherNode!=null) { if(isLeft) { fatherNode.mLeftNode=TempNode; } else { fatherNode.mRightNode=TempNode; } } else { mRootNode=TempNode; } } else { boolean addIsBiger=subTreeNode.mElement.compareTo(element)<0;//泛型實現了對比接口 if(addIsBiger) { add(element,subTreeNode.mRightNode,subTreeNode,false); } else { add(element,subTreeNode.mLeftNode,subTreeNode,true); } RotationAndHeight(subTreeNode,fatherNode,isLeft); } } //問題:匹配一個樹,組合:匹配根,匹配左樹,匹配右樹。基本問題:節點就是。 //空:直接刪,副節點設空。 有單子樹。修改數據。左右樹都有,找高度更高的換數據。 被換節點必定是葉子,刪除葉子,父節點設空。 //旋轉和高度問題。要求 //1.傳過來的根節點參數的左右子樹高度是正確的。那麼RotationAndHeight函數就就能夠往上遞歸正確的計算高度和進行修正。 //2.remove函數往下遞歸時,保證是逐層進行的。那麼才能保證RotationAndHeight會逐層往上。 public void remove(T element,MyAVLNode<T> subTreeNode,MyAVLNode<T> fatherNode,boolean isLeft) { if(subTreeNode==null) { return; } int compareRet=subTreeNode.mElement.compareTo(element); if(compareRet==0) { if(subTreeNode.mLeftNode==null && subTreeNode.mRightNode==null) { if(fatherNode!=null) if(isLeft) { fatherNode.mLeftNode=null; } else { fatherNode.mRightNode=null; } else { mRootNode=null; } } else if(subTreeNode.mLeftNode==null || subTreeNode.mRightNode==null) { if(fatherNode!=null) { if(isLeft) { fatherNode.mLeftNode=subTreeNode.mLeftNode==null?subTreeNode.mRightNode:subTreeNode.mLeftNode; } else { fatherNode.mRightNode=subTreeNode.mLeftNode==null?subTreeNode.mRightNode:subTreeNode.mLeftNode; } } else { mRootNode=subTreeNode.mLeftNode==null?subTreeNode.mRightNode:subTreeNode.mLeftNode; } } else { MyAVLNode<T> minnode= findMin(subTreeNode.mRightNode); subTreeNode.mElement=minnode.mElement; //能夠肯定是葉子,要效率高點。就能夠新寫個findMin,返回父節點。直接寫代碼刪除。不須要這裏一直遞歸。但對於修正平衡就沒有辦法往上遞歸了。 remove(minnode.mElement, subTreeNode.mRightNode, subTreeNode, false); RotationAndHeight(subTreeNode,fatherNode,isLeft); } } else if(compareRet>0)//根節點更大。 { remove(element, subTreeNode.mLeftNode,subTreeNode, true); RotationAndHeight(subTreeNode,fatherNode,isLeft); } else { remove(element, subTreeNode.mRightNode,subTreeNode, false); RotationAndHeight(subTreeNode,fatherNode,isLeft); } } public MyAVLNode<T> findMax(MyAVLNode<T> subTreeNode) { MyAVLNode<T> tempRet=subTreeNode; while(tempRet!=null && tempRet.mRightNode!=null) { tempRet=tempRet.mRightNode; } return tempRet; } public MyAVLNode<T> findMin(MyAVLNode<T> subTreeNode) { MyAVLNode<T> tempRet=subTreeNode; while(tempRet!=null && tempRet.mLeftNode!=null) { tempRet=tempRet.mLeftNode; } return tempRet; } private void RotationAndHeight(MyAVLNode<T> subTreeNode,MyAVLNode<T> fatherNode,boolean isLeft) { //高度差=2 旋轉,從新計算高度。 //高度差<2. 根節點是否須要更新高度。 //高度差>2 .錯誤。 //斷言對於快速開發和測試很是重要,並且減小正式版的編譯代碼和速度。不過若是有必要後期仍是要寫入到日誌中. int leftHeight=subTreeNode.mLeftNode==null?0:subTreeNode.mLeftNode.mHeight; int rightHeight=subTreeNode.mRightNode==null?0:subTreeNode.mRightNode.mHeight; int maxSubTreeHeight=Math.max(leftHeight, rightHeight); assert(Math.abs(leftHeight-rightHeight)<=2) : "why .left compare to right is error"; if(Math.abs(leftHeight-rightHeight)==2) { int ll=0,lr=0,rl=0,rr=0; int treetype=0; if(leftHeight-rightHeight==2) { assert(subTreeNode.mLeftNode!=null):"no way"; ll=subTreeNode.mLeftNode.mLeftNode==null?0:subTreeNode.mLeftNode.mLeftNode.mHeight; lr=subTreeNode.mLeftNode.mRightNode==null?0:subTreeNode.mLeftNode.mRightNode.mHeight; if(ll>=lr)//這裏用等號的話,刪除的時候,能夠用更簡單的單轉。 { MyAVLNode<T> newRoot= subTreeNode.mLeftNode; subTreeNode.mLeftNode=newRoot.mRightNode; newRoot.mRightNode=subTreeNode; newRoot.mRightNode.mHeight=newRoot.mRightNode.mHeight-1; if(isLeft && fatherNode!=null) { fatherNode.mLeftNode=newRoot; } else if (!isLeft && fatherNode!=null) { fatherNode.mRightNode=newRoot; } else { mRootNode=newRoot; } } else { MyAVLNode<T> newRoot= subTreeNode.mLeftNode.mRightNode; subTreeNode.mLeftNode.mRightNode=newRoot.mLeftNode; newRoot.mLeftNode= subTreeNode.mLeftNode; subTreeNode.mLeftNode=newRoot.mRightNode; newRoot.mRightNode=subTreeNode; newRoot.mHeight++; newRoot.mLeftNode.mHeight--; newRoot.mRightNode.mHeight--; if(isLeft && fatherNode!=null) { fatherNode.mLeftNode=newRoot; } else if (!isLeft && fatherNode!=null) { fatherNode.mRightNode=newRoot; } else { mRootNode=newRoot; } } } else { assert(subTreeNode.mRightNode!=null):"no way"; rl=subTreeNode.mRightNode.mLeftNode==null?0:subTreeNode.mRightNode.mLeftNode.mHeight; rr=subTreeNode.mRightNode.mLeftNode==null?0:subTreeNode.mRightNode.mLeftNode.mHeight; if(rr>=rl)//這裏用等號的話,刪除的時候,能夠用更簡單的單轉。 { MyAVLNode<T> newRoot= subTreeNode.mRightNode; subTreeNode.mRightNode=newRoot.mLeftNode; newRoot.mLeftNode=subTreeNode; newRoot.mLeftNode.mHeight=newRoot.mLeftNode.mHeight-1; if(isLeft && fatherNode!=null) { fatherNode.mLeftNode=newRoot; } else if (!isLeft && fatherNode!=null) { fatherNode.mRightNode=newRoot; } else { mRootNode=newRoot; } } else { MyAVLNode<T> newRoot= subTreeNode.mRightNode.mLeftNode; subTreeNode.mRightNode.mLeftNode=newRoot.mRightNode; newRoot.mRightNode= subTreeNode.mRightNode; subTreeNode.mRightNode=newRoot.mLeftNode; newRoot.mLeftNode=subTreeNode; newRoot.mHeight++; newRoot.mRightNode.mHeight--; newRoot.mLeftNode.mHeight--; if(isLeft && fatherNode!=null) { fatherNode.mLeftNode=newRoot; } else if (!isLeft && fatherNode!=null) { fatherNode.mRightNode=newRoot; } else { mRootNode=newRoot; } } } } else if(Math.abs(leftHeight-rightHeight)==1) { assert((subTreeNode.mHeight-maxSubTreeHeight==0 || subTreeNode.mHeight-maxSubTreeHeight==1)) : "top node's height was wrong compare with left and right substree"; if(subTreeNode.mHeight-maxSubTreeHeight==0) { subTreeNode.mHeight++; } } } //add:主要是4個問題。1,正確插入位置。2,更新新插入點的父節點數據。3.更新新插入點的父節點數據的高度,4.新插入點的父節點是否平衡 // 1,通常處理。2,採用遞歸+方法帶返回值:新節點,那麼當遞歸返回上層時,可給返回的新插入點賦予正確的父節點。 // 3.4,保證遞歸方法是逐層進行,並保證新插入點的左右子樹高度正確。那麼遞歸回來,會保證新插入點及其全部父節點左右子樹高度正確以及都檢測旋轉。 public MyAVLNode<T> add(T element,MyAVLNode<T> addToThisNode) { MyAVLNode<T> ret=null; if(addToThisNode==null)//某條件下,成了基本問題 { ret= new MyAVLNode<T>(element, null, null); } else//其餘條件下,用更小規模問題來組合 { int addIsBigger=element.compareTo(addToThisNode.mElement); if(addIsBigger<0) { addToThisNode.mLeftNode= add(element, addToThisNode.mLeftNode); } else { addToThisNode.mRightNode=add(element, addToThisNode.mRightNode); } ret=addToThisNode; } //檢查高度,檢查是否須要旋轉。 ret=reHeightAndBalance(ret); if(addToThisNode==mRootNode) { mRootNode=ret; } return ret; } //remove 和add基本思路同樣。稍微複雜一點. //有目標點有3種狀況,1,無左右子樹,那麼刪除葉子,2,左右一個爲空。那麼跳過要刪除點,和左右子樹想相連。 //3.左右都不爲空,本質和1是同樣。刪除右子樹的最小值節點,也就是一個葉子。並把葉子的數據給目標點。 //返回當前子樹最高頂點。 public MyAVLNode<T> remove(T element,MyAVLNode<T> removeThisNode) { MyAVLNode<T> ret=null; if(removeThisNode==null) { //空樹或者沒找到要刪除的點,什麼都不作,並返回null,由於原本就是null,返回給這個節點的父節點NULL,等於什麼都沒作。 ret=null; } else { int removeIsBigger=element.compareTo(removeThisNode.mElement); if(removeIsBigger==0)////某條件下,成了基本問題 { if(removeThisNode.mLeftNode==null && removeThisNode.mRightNode==null) { removeThisNode=null;//其實這句沒用,棧內的一個變量賦值爲空而已,也沒達到手動釋放的堆內存,並且是java了。不必勞心內存問題。 ret=null; } else if(removeThisNode.mLeftNode==null || removeThisNode.mRightNode==null)//不能夠寫成 != || != .由於會包含 != && !=.而如今這樣寫,額外包含的,在上面已經被排除了. { ret =removeThisNode.mLeftNode==null?removeThisNode.mRightNode:removeThisNode.mLeftNode; } else { MyAVLNode<T> minNode= findMin(removeThisNode.mRightNode); removeThisNode.mElement= minNode.mElement; removeThisNode.mRightNode=remove(minNode.mElement, removeThisNode.mRightNode); ret=removeThisNode; //return remove(minNode.mElement, removeThisNode.mRightNode);//原寫代碼 嚴重錯誤。 } } else if(removeIsBigger<0) { removeThisNode.mLeftNode= remove(element, removeThisNode.mLeftNode); ret=removeThisNode; } else { removeThisNode.mRightNode= remove(element, removeThisNode.mRightNode); ret=removeThisNode; } } ret=reHeightAndBalance(ret); if(removeThisNode==mRootNode) { mRootNode=ret; } return ret; } //rotation: add .可重新加入點算起,add方法已是逐步升入,那麼會原路返回 //remove:1. 雙枝或空枝,逐步過來,最低點有null狀況。返回nlll就好。 2.單枝狀況,還好, 檢查返回值的高度和平衡。再從新返回. private MyAVLNode<T> reHeightAndBalance(MyAVLNode<T> subTreeNode) { MyAVLNode<T> ret=subTreeNode; //null:返回 。 要平衡:平衡。算高度。 if(subTreeNode==null) { ret=null; } else { int leftHeight=subTreeNode.mLeftNode==null?0:subTreeNode.mLeftNode.mHeight; int rightHeight=subTreeNode.mRightNode==null?0:subTreeNode.mRightNode.mHeight; int maxSubTreeHeight=Math.max(leftHeight, rightHeight); assert(Math.abs(leftHeight-rightHeight)<=2) : "why .left compare to right is error"; if(Math.abs(leftHeight-rightHeight)==2) { int ll=0,lr=0,rl=0,rr=0; int treetype=0; if(leftHeight-rightHeight==2) { assert(subTreeNode.mLeftNode!=null):"no way"; ll=subTreeNode.mLeftNode.mLeftNode==null?0:subTreeNode.mLeftNode.mLeftNode.mHeight; lr=subTreeNode.mLeftNode.mRightNode==null?0:subTreeNode.mLeftNode.mRightNode.mHeight; if(ll>=lr)//這裏用等號的話,刪除的時候,能夠用更簡單的單轉。 { MyAVLNode<T> newRoot= subTreeNode.mLeftNode; subTreeNode.mLeftNode=newRoot.mRightNode; newRoot.mRightNode=subTreeNode; newRoot.mRightNode.mHeight=newRoot.mRightNode.mHeight-1; ret=newRoot; } else { MyAVLNode<T> newRoot= subTreeNode.mLeftNode.mRightNode; subTreeNode.mLeftNode.mRightNode=newRoot.mLeftNode; newRoot.mLeftNode= subTreeNode.mLeftNode; subTreeNode.mLeftNode=newRoot.mRightNode; newRoot.mRightNode=subTreeNode; newRoot.mHeight++; newRoot.mLeftNode.mHeight--; newRoot.mRightNode.mHeight--; ret=newRoot; } } else { assert(subTreeNode.mRightNode!=null):"no way"; rl=subTreeNode.mRightNode.mLeftNode==null?0:subTreeNode.mRightNode.mLeftNode.mHeight; rr=subTreeNode.mRightNode.mLeftNode==null?0:subTreeNode.mRightNode.mLeftNode.mHeight; if(rr>=rl)//這裏用等號的話,刪除的時候,能夠用更簡單的單轉。 { MyAVLNode<T> newRoot= subTreeNode.mRightNode; subTreeNode.mRightNode=newRoot.mLeftNode; newRoot.mLeftNode=subTreeNode; newRoot.mLeftNode.mHeight=newRoot.mLeftNode.mHeight-1; ret=newRoot; } else { MyAVLNode<T> newRoot= subTreeNode.mRightNode.mLeftNode; subTreeNode.mRightNode.mLeftNode=newRoot.mRightNode; newRoot.mRightNode= subTreeNode.mRightNode; subTreeNode.mRightNode=newRoot.mLeftNode; newRoot.mLeftNode=subTreeNode; newRoot.mHeight++; newRoot.mRightNode.mHeight--; newRoot.mLeftNode.mHeight--; ret=newRoot; } } } else if(Math.abs(leftHeight-rightHeight)==1) { assert((subTreeNode.mHeight-maxSubTreeHeight==0 || subTreeNode.mHeight-maxSubTreeHeight==1)) : "top node's height was wrong compare with left and right substree"; if(subTreeNode.mHeight-maxSubTreeHeight==0) { subTreeNode.mHeight++; } ret=subTreeNode; } } return ret; } //type:0:first print parent node .1 left children ,parent, right children. 2:left children ,right children parent. public void printTree(int type) { printTree(mRootNode, type,""); } //打印樹:組合:打印左樹,打印節點,打印右樹。基本狀況:葉子。 private void printTree(MyAVLNode<T> node,int type,String space) { if(node==null) { System.out.println(""); return; } String leafInfo=space+node.mElement.toString()+"["+node.mHeight+"]"; String newSpace=space+" "; if(node.mLeftNode==null && node.mRightNode==null) { System.out.println(leafInfo); } else { if(type==0) { System.out.println(leafInfo); if(node.mLeftNode!=null) { printTree(node.mLeftNode, type,newSpace); } if(node.mRightNode!=null) { printTree(node.mRightNode, type,newSpace); } } if(type==1) { if(node.mLeftNode!=null) { printTree(node.mLeftNode, type,newSpace); } System.out.println(leafInfo); if(node.mRightNode!=null) { printTree(node.mRightNode, type,newSpace); } } if(type==2) { if(node.mLeftNode!=null) { printTree(node.mLeftNode, type,newSpace); } if(node.mRightNode!=null) { printTree(node.mRightNode, type,newSpace); } System.out.println(leafInfo); } if(type==3) { if(node.mRightNode!=null) { printTree(node.mRightNode, type,newSpace); } System.out.println(leafInfo); if(node.mLeftNode!=null) { printTree(node.mLeftNode, type,newSpace); } } } } public MyAVLNode<T> mRootNode=null; }
測試代碼接口
public static class AVL { public static void test() { MyAVL<Integer> mytreeAvl=new MyAVL<Integer>(); // mytreeAvl.add(20, mytreeAvl.mRootNode,null,true); // mytreeAvl.add(7, mytreeAvl.mRootNode,null,true); // //mytreeAvl.add(56, mytreeAvl.mRootNode,null,true); // //mytreeAvl.add(5, mytreeAvl.mRootNode,null,true); // //mytreeAvl.add(9, mytreeAvl.mRootNode,null,true); // //mytreeAvl.add(3, mytreeAvl.mRootNode,null,true); //// mytreeAvl.add(12, mytreeAvl.mRootNode,null,true); //// mytreeAvl.add(64, mytreeAvl.mRootNode,null,true); //// mytreeAvl.add(33, mytreeAvl.mRootNode,null,true); //// mytreeAvl.add(15, mytreeAvl.mRootNode,null,true); mytreeAvl.add(20, mytreeAvl.mRootNode); mytreeAvl.add(7, mytreeAvl.mRootNode); mytreeAvl.add(56, mytreeAvl.mRootNode); mytreeAvl.add(5, mytreeAvl.mRootNode); mytreeAvl.add(9, mytreeAvl.mRootNode); mytreeAvl.add(3, mytreeAvl.mRootNode); mytreeAvl.add(12, mytreeAvl.mRootNode); // mytreeAvl.add(64, mytreeAvl.mRootNode,null,true); // mytreeAvl.add(33, mytreeAvl.mRootNode,null,true); // mytreeAvl.add(15, mytreeAvl.mRootNode,null,true); //mytreeAvl.remove(20, mytreeAvl.mRootNode, null, true); //mytreeAvl.remove(5, mytreeAvl.mRootNode, null, true); mytreeAvl.printTree(3); } }