樹
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)的代碼實現
Node 需要有數據項,有該類對象的左節點,右節點,還可以包含其他的數據項。實現大致如下:
public class Node {
/**
* 數據項
*/
int data;
/**
* 其他數據
*/
int otherData;
/**
* 左節點
*/
Node leftChild;
/**
* 右節點
*/
Node rightChild;
public Node() {
}
}
Tree和Node實現后,那么便可以實現里面的操作方法了。
find查找過程如圖

根據上圖可以看出查找一個節點,最多比較次數為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插入,插入和查找基本過程差不多,仍然是比較數據項大小,小了放在左側,大了放其右側。

其代碼如:
/**
* @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;
}
}
}
}
}
遍歷二叉搜索樹
二叉樹的中序遍歷過程是調用自身左子樹,然后訪問節點,在調用自身右子樹。遞歸代碼如下。而前序和后序的遍歷只需要把中序遍歷
中的調用自身的遞歸和訪問節點(就是打印那一行)翻翻順序就ok了。
/**
* 遞歸遍歷二叉樹(中序)
*/
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個子節點,則需要先找到待刪除節點的后續節點,即是比待刪除節點次高的節點。
如圖:

刪除過程就是:87位后續節點,為50的右節點。62為87的左節點。89位87的右節點。

刪除:后續節點在左側時:

查找到后續節點是77,則50的右節點為77,79變成87的左節點,93還是83的右節點。62變成77的左節點。

刪除代碼實現:
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;
}
刪除節點:
/**
* 刪除節點
* @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;
}
找最大最小值:

尋找最大最小值 這個就簡單了,從根節點一直找左節點直到沒有左子節點,那么這個值就是最小值,反之就是最大值。
4.小結:
代碼:
http://pan.baidu.com/s/1miz8ocC
https://github.com/galibujianbusana/MyErChaShu

