BST是一種特殊的二叉樹,其定義以下:java
BST :在二叉樹的基礎上,任意一個節點知足大於左子樹中的全部節點,小於右子樹中的全部節點。node
注意點:git
private Node add(Node node,E e)
:向以node
爲根節點的BST中插入新的元素e
,並返回新的根節點。github
private Node add(Node node,E e){ if(node==null){ return new Node(e); } if(e.compareTo(node.e)<0){//左邊插入。其他不變 node.left=add(node.left,e); }else if(e.compareTo(node.e)){ node.right=add(node.right,e); } //相等,忽略。 return node; } public void add(E e){//從根節點開始找。 root=add(root,e); }
最小值位於節點內部時,其後是一個右子樹,此時刪除節點後,將剩餘子樹接到刪除節點父節點位置,做爲他的左子樹。(刪除最大值同理)--知足BST性質。數據結構
private Node removeMin(Node node)
刪除已node爲根節點的BST的最小節點,並返回刪除節點後新的BST的根節點。函數
public E removeMin(){ E min=getMin(); root=removeMin(root); return min; } private Node removeMin(Node node){ //found it,左子樹的終點 if(node.left==null){ Node rightNode=node.right; size--; return rightNode; } node.left=removeMin(node.left); return node; }
刪除的節點只有右孩子 :刪除當前節點,而後把右子樹放置到當前節點的位置,至關於delMin性能
刪除節點只有左孩子:刪除當前節點,而後把左子樹放置到當前節點的位置,相等於deMaxcode
待刪除元素左右孩子都有:找到比待刪除節點大的最小節點(BST性質),用這個節點頂替代刪除節點的位置。blog
//刪除已node爲根的BST中值爲e的節點,並返回刪除以後新的BST的根。 private Node remove(Node node,E e){ if(node==null)return null; if(e.comapreTo(node.e)<0){ node.left=remove(node.left,e); return node; } else if(e.compareTo(node.e)>0){ node.right=remove(node.right,e); return node; } //找到待刪除元素(e.compareTo(node.e)==0),體會函數執行到這兒的含義。 else{ //待刪除節點左子樹爲null if(node.left==null){ Node rightNode=node.right; node.right=null; size--; return rightNode; } if(node.right==null){ Node leftNode=node.left; node.left=null; size--; return leftNode; } //兩邊都不爲null。 Node successor=minimum(node.right); sussessor.right=removeMin(node.right);//右邊最小的。 sussessor.left=node.right; return sussessor; } } public void remove(E e) { root = remove(root, e); }
private boolean contains(Node node,E e){ if(node==null) return false; if(e.compareTo(node.e)<0){ return contains(node.left,e); }else if(e.compareTo(node.e)>0){ return contains(node.right,e); }else{//先找到,這點和刪除的邏輯很像 return true; } } public boolean contains(E e){ return contains(root,e); }
BST的完整源碼請點擊這兒遞歸
AVL樹在BST的基礎上增長了平衡性約束。新增的操做(旋轉與高度更新)都是爲了知足平衡性約束
對於任意一個節點,左子樹與右子樹的高度差不能超過1
爲了保證樹的高度與與節點總數成logN
的關係,而通常的操做(add,delete,contains)都與樹的高度有關,例如,堆是徹底二叉樹也是平衡二叉樹,對堆的各類操做,時間複雜度都是log(N)
插入就是在失敗的查找的基礎上再操做,插入過程當中維持有序性和平衡性
的核心操做:旋轉。
咱們只須要關注第一個不平衡的節點,而後再遞歸檢查就OK,此時,只須要考察該節點與其孩子節點,其孩子的孩子節點(三代)的關係,由於這是致使不平衡的最小單元。
在節點的左孩子的左側,插入新節點,致使節點失衡,LL,此時經過右旋轉來解決,以中間節點爲中心,旋轉,這樣可以同時知足有順序性和平衡性。
//y:第一個不平衡的節點,返回旋轉後的根節點x private Node rotateRight(Node y ){ Node x=y.left; Node T3=x.right; x.right=y; y.left=T3; //對x和y的位置進行調整,相應的高度也須要更新!! y.height=calHeight(y); x.height=calHeight(x); return x; } private Node add(Node node,K key, V va){ //LL:在node的左側的左側添加的節點致使node不平衡。 if(balance>1&&calBalance(node,left)>=0){//calBalance:left的高度-right高度 return rotateRight(node); } }
在節點的右側的右側加入新節點,致使節點失衡,RR,採用左旋轉解決,旋轉中心爲中間節點x.
//y是第一個失衡的節點,返回旋轉後的根節點 private Node rotateLeft(Node y){ Node x=y.right; Node T3=x.left; x.left=y; y.right=T3; //更新高度 x.height=calHeight(x); y.height=calHeight(y); return x; } private Node add(Node node, K key,V val){ //.... if(balance<-1&&calBalance(node.right)<=0){//balance的定義L-R return rotateLeft(node); } }
在節點左孩子的右側加入新節點(LR),致使AVL樹失衡,調整方式:先左旋轉(LL)後右旋轉
private Node add(Node node,K key ,V val){//牢記要知足有序性+平衡性 //.... if(balance>1&&calBalance(node.left)<0){//以Z爲中心。 node.left=rotateLeft(node.left); return rotateRight(node); } //.... }
在節點右孩子的左側插入新節點致使樹失衡,RL.調整方式:先右旋轉(RR)後左選擇.
private Node add(Node node,K key ,V val){ //*... if(balance<-1&&calBalance(Node.right)>0){ node.right=rotateRight(node.right);//z爲中心。 return rotateLeft(node); } //.... }
BST的刪除操做已經保證有序性了,再此基礎上還須要考慮LL,LR,RR,RL着四種不平衡狀態。
public V remove(K key){ Node node=getNode(root,key); if(node!=null){ root=reomve(root,key); return node.val; } return null; } private Node remove(Node node , K key){ //代碼與BST的刪除邏輯徹底同樣,只是爲了調整平衡性,用retNode來保存刪除後的頭結點。 return rotateToReBalance(retNode); } //判斷當前節點是否須要旋轉,並執行相應的旋轉操做。 private Node rotateToReBalance(Node node){ if(node==null) return null; node.height=calHeight(node); int blc=calBalance(node); if(blc>1&&calBlanece(node.left)>=0){ return rotateRight(node); } if(blc<-1&&calBalance(node.right)<=0){ return rotateLeft(node); } if(blc>1&&calBalance(node.left)<0){ node.left=rotateLeft(node.left); return rotateRight(node); } if(blc<-1&&calBalance(node.right)>0){ node.right=rotateRight(node.right); return rotateLeft(node); } //不須要維護平衡, return node; }
AVL樹的完整源碼請點擊這兒
定義:
2-3樹知足BST的基本性質(有序性),節點能夠存放一個元素(二節點),也能夠存放兩個元素(三節點)。
性質:2-3樹是一顆絕對平衡的樹。從根節點到任意葉子節點之間具備相同的節點數。
2-3樹的插入過程就是不斷造成3節點(則原來是2節點)、4節點(有4個分叉,即3個元素)的過程,而後拆分4節點爲三個2節點的過程。目的:知足2-3絕對平衡的性質。
當向2節點插入時,帶插入位置是左節點,則不調整,是右節點則須要左旋轉+顏色翻轉,以知足紅黑樹的定義。
左旋轉過程以下
private Node rotateLeft(Node node) { // 暫存節點 Node x = node.right; // 左旋轉 node.right = x.left; x.left = node; // 更新顏色 x.color = node.color; node.color = RED; return x; }
private Node rotateRight(Node node) { // 暫存節點 Node x = node.left; Node T1 = x.right; // 右旋轉 node.left = T1; x.right = node; // 顏色更新 x.color = node.color; node.color = RED; return x; }
當向3節點插入時,須要依次通過左旋轉,右旋轉,顏色翻轉等過程。
紅黑樹的源碼請點擊這兒
綜合增刪改查全部的操做,紅黑樹是平均性能最好的。AVL樹的插入和刪除過於複雜,查詢較多時,AVL樹比較合適。普通的BST對於有序數據就退化爲鏈表。