樹
1.樹的優勢
有序數組: 查找很快,二分法實現的查找所須要的時間爲O(logN),遍歷也很快,可是在有序數組中插入,刪除卻須要先 找到位置,
在把數組部分元素後移,效率並不高。
鏈表: 鏈表的插入和刪除都是很快速的,僅僅須要改變下引用值就好了,時間僅爲O(1),可是在鏈表中查找數據卻須要
遍歷全部的元素, 這個效率有些慢了。
樹的優勢: 樹結合了有序數組和鏈表的優勢,能夠實現快速的查找,也能夠快速的刪除,查找。
樹的一些專用術語:
路徑:
順着鏈接節點的邊從一個節點到另外一個節點的,所通過的全部節點的順序排列就是路徑。
根:
根便是樹的頂端,一個樹有且只有一個根,從根到全部節點的路徑有且只有一條。
父節點:
每個節點的鏈接的上一個節點便是該節點的父節點。
子節點;
每個節點的向下鏈接的節點便是改節點的子節點
子樹:
每一個節點均可以認爲是一個樹的根,
葉節點:
就是沒有子節點的節點
層:
一個節點的層數,從根節點到該節點有多少代,就是多少層
二叉樹:
樹種的節點能夠有多個節點,二叉樹是最多隻能有2個節點的樹。二叉樹的兩個節點被稱爲左子節點和右子節點。
二叉樹的節點是最多有2個子節點,但並非必須有2個子節點。
平衡樹和非平衡樹:
若是一個樹中存在一個或多個的子樹,只有右子節點,或左子節點,那麼這個樹就是非平衡樹。
2.二叉搜索樹:
根節點的左右2個節點,小於根節點在放在左側,大於根節點的放在右側。
<1>插入
<2>查找
<3>遍歷
1.中序遍歷
(1)調用自身遍歷節點的左子樹
(2)訪問這個節點
(3)調用自身遍歷節點的右子樹
如上圖遍歷過程:35,38,40,45,50,67,83
2.前序遍歷
(1)訪問這個節點
(2)調用自身遍歷節點的左子樹
(3)調用自身遍歷節點的右子樹
如上圖遍歷過程:45,38,35,40,67,50,83
3.後序遍歷
(1)調用自身遍歷節點的左子樹
(2)調用自身遍歷節點的右子樹
(3)訪問這個節點
如上圖遍歷過程:35,40,38,50,83,67,45
<4>刪除
(1)刪除葉節點(沒有子節點的)
(2)刪除節點(一個子節點的)
(3)刪除節點(二個子節點的)
<5>查找最大最小值
3.二叉搜索樹的代碼實現
樹(Tree)的代碼實現:
Tree 只須要有根節點,便可訪問全部的子節點,這裏能夠簡單的定義該類,只有一個變量Root.Root類型爲Node(節點對象)
該類能夠實現一些操做方法大體以下:
public class Tree { Node root; public Tree() { } /** * 刪除節點 * @param key */ public void deldte( int key){ } /** * 查找節點 * * @param key */ public Node find(int key){ return null; } /** * * * @param key 插入值 * @param otherdata 插入的其餘數據 */ public void insert( int key,int otherdata){ } /** * 遍歷二叉樹 */ public void disPlayTree(Node node){ }
節點(Node)的代碼實現java
Node 須要有數據項,有該類對象的左節點,右節點,還能夠包含其餘的數據項。實現大體以下:
node
public class Node { /** * 數據項 */ int data; /** * 其餘數據 */ int otherData; /** * 左節點 */ Node leftChild; /** * 右節點 */ Node rightChild; public Node() { } }
Tree和Node實現後,那麼即可以實現裏面的操做方法了。git
find查找過程如圖
![](http://static.javashuo.com/static/loading.gif)
根據上圖能夠看出查找一個節點,最多比較次數爲Tree的層數,其代碼以下:
/** * 查找節點 * * @param key 查找的值,在該代碼中爲Node.data */ public Node find(int key){ Node current =root; while (current.data!=key){ /** * 小於當前節點的值,去left查找,不然去right */ if(key<current.data){ current=current.leftChild; }else { current=current.rightChild; } /** * 沒查找到 */ if(current==null){ return null; } } return current; }
insert插入,插入和查找基本過程差很少,仍然是比較數據項大小,小了放在左側,大了放其右側。github
其代碼如:
/** * @param key 插入值 node.data * @param otherdata 插入的其餘數據 node.otherdata */ public void insert( int key,int otherdata){ Node newNode=new Node(); newNode.data=key; newNode.otherData=otherdata; if(root==null){ root=newNode; }else{ Node current=root; Node parent; while (true) { parent=current; if(key<current.data){ current=current.leftChild; if(current==null){ parent.leftChild=newNode; return; } }else{ current=current.rightChild; if(current==null){ parent.rightChild=newNode; return; } } } } }
遍歷二叉搜索樹數組
二叉樹的中序遍歷過程是調用自身左子樹,而後訪問節點,在調用自身右子樹。遞歸代碼以下。而前序和後序的遍歷只須要把中序遍歷ide
中的調用自身的遞歸和訪問節點(就是打印那一行)翻翻順序就ok了。spa
/** * 遞歸遍歷二叉樹(中序) */ public void disPlayTree(Node node){ if(node!=null){ if(node.leftChild!=null){ disPlayTree(node.leftChild); } Log.d("", "二叉樹遍歷: "+node.data); if(node.rightChild!=null){ disPlayTree(node.rightChild); } } }
刪除:delete節點。若是待刪除節點是葉節點(沒有子節點),值直接把刪除節點賦值爲null便可。
若是有一個子節點也簡單,待刪除的節點在刪除節點的左側(右側),則把待刪除節點的子樹賦值給待刪除節點父節點的
左側(右側)。
刪除:若是刪除節點右2個子節點,則須要先找到待刪除節點的後續節點,便是比待刪除節點次高的節點。
如圖:
![](http://static.javashuo.com/static/loading.gif)
刪除過程就是:87位後續節點,爲50的右節點。62爲87的左節點。89位87的右節點。
![](http://static.javashuo.com/static/loading.gif)
刪除:後續節點在左側時:
![](http://static.javashuo.com/static/loading.gif)
查找到後續節點是77,則50的右節點爲77,79變成87的左節點,93仍是83的右節點。62變成77的左節點。
![](http://static.javashuo.com/static/loading.gif)
刪除代碼實現:
1.獲取後續節點
/** * 獲取後序節點 */ public Node getSuccessor(Node delNode){ Node successorParent =delNode; Node successor=delNode; Node current=delNode.rightChild; while(current!=null){ successorParent=successor; successor=current; current=current.leftChild; } if(successor!=delNode.rightChild){ successorParent.leftChild=successor.rightChild; successor.rightChild=delNode.rightChild; } Log.d("二叉樹遍歷", "getSuccessor: "+successor.data); return successor; }
刪除節點:code
/** * 刪除節點 * @param key */ public boolean deldte( int key){ Node current=root; Node parent=root; boolean isLeftChild=true; /** * 先把刪除值的Node找出來 */ while(current.data!=key){ parent=current; if(key<current.data){ isLeftChild=true; current=current.leftChild; }else { isLeftChild=false; current=current.rightChild; } if(current==null){ return false; } } // while結束,查找到刪除節點,就是current /** * 若是刪除節點是葉節點 */ if(current.leftChild==null&¤t.rightChild==null){ if(current==root){ root=null; }else if(isLeftChild){ parent.leftChild=null; }else{ parent.rightChild=null; } } /** * 若是刪除的節點沒有rightChild,只有leftChild */ else if(current.rightChild==null){ if(current==root){ root=current.leftChild; } else if(isLeftChild){ parent.leftChild=current.leftChild; } else { parent.rightChild=current.leftChild; } } /** * 若是刪除的節點只有rightChild */ else if(current.leftChild==null){ if(current==root){ root=current.rightChild; } else if(isLeftChild){ parent.leftChild=current.rightChild; }else{ parent.rightChild=current.rightChild; } } /** * 若是刪除點有2個節點 */ else { /** * 獲取後續節點 */ Node successor=getSuccessor(current); if(current==root){ root=successor; }else if( isLeftChild){ parent.leftChild=successor; }else{ parent.rightChild=successor; } successor.leftChild=current.leftChild; } return true; }
找最大最小值:對象
![](http://static.javashuo.com/static/loading.gif)
尋找最大最小值 這個就簡單了,從根節點一直找左節點直到沒有左子節點,那麼這個值就是最小值,反之就是最大值。
4.小結:
代碼:
http://pan.baidu.com/s/1miz8ocC
https://github.com/galibujianbusana/MyErChaShu