二叉查找樹(Binary Search Tree),(又:二叉搜索樹,二叉排序樹)它或者是一棵空樹,或者是具有下列性質的二叉樹:
若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
它的左、右子樹也分別爲二叉排序樹。
其實說白點就是一顆二叉樹滿足了左子節點小於根節點元素,右子節點大於根節點元素,這樣的二叉樹就是二叉搜索樹,二叉搜索樹實現符號表,具有插入、查找的高效性,對其優化可以生成平衡二叉樹,紅黑樹等更高效的查找和插入數據結構,是一個優秀的基礎算法。
看下其數據的結構
這是一個簡單的二叉搜索樹結構,滿足了基本的需求
class Node{
int key;
int value;
Node left,right;
// int N;
Node(int key,int value){
this.key=key;
this.value=value;
}
}
看下樹中方法涉及到插入、查找、刪除基本操作
class Tree{
Node root; //儲存根節點
//查找指定key元素返回節點
public Node find(int key){
return null;
}
//插入元素
public void insert(int key,int value){
}
//刪除元素
public void delete(int key){
}
//返回node下最小節點
public Node min(Node node){
}
//刪除該節點下的最小節點
public void deleteMin(Node node){
}
//刪除該節點下最大節點
public void deleteMax(Node node){
}
}
查找操作和二分查找類似,將key和節點的key比較,如果小於,那麼就在Left Node節點查找,如果大於,則在Right Node節點查找,如果相等,直接返回Value。
public Node find(int key){
// 查找也是從根元素出發,找不到返回null
Node currentNode=root;
while(currentNode!=null&¤tNode.key!=key){
if(key<currentNode.key)
{
currentNode=currentNode.left;
}else
{
currentNode=currentNode.right;
}
}
return currentNode;
}
插入過程非常簡單,很類似與二叉樹搜索樹的查找過程。當需要插入一個新結點時,從根節點開始,迭代或者遞歸向下移動,直到遇到一個空的指針NIL,需要插入的值即被存儲在該結點位置。
public void insert(int key,int value){
//如果根節點沒有元素,此節點便爲根節點
if(root==null){
root=new Node(key,value);
return;
}
//比較左右節點元素,從root開始
Node parentNode=root;//當前元素父元素
Node currentNode=root;//當前元素
while(currentNode!=null){ //一直遍歷找到一個空元素爲止
parentNode=currentNode;
if(key<currentNode.key){ //如果key小於該元素值,往左找,相反則往右
currentNode=currentNode.left;
}else{
currentNode=currentNode.right;
}
}
if(key<parentNode.key){ //插入元素
System.out.println(parentNode.key+"的左邊可以插入"+key);
parentNode.left=new Node(key,value);
}else{
System.out.println(parentNode.key+"的右邊可以插入"+key);
parentNode.right=new Node(key,value);
}
}
返回該節點最小節點,就是一直遍歷往左遍歷直到找到其左子節點爲空那麼當前節點就是最小的。
public Node min(Node node){ //返回node下最小節點
//當前節點爲空直接返回,不爲空找到其左子節點爲空爲止
Node currentNode=node;
if(currentNode==null)
return null;
while(currentNode.left!=null){
currentNode=currentNode.left;
}
return currentNode;
}
刪除元素操作在二叉樹的操作中應該是比較複雜的。首先來看下比較簡單的刪除最大最小值得方法。
以刪除最小值爲例,我們首先找到最小值,及最左邊左子樹爲空的節點,然後返回其右子樹作爲新的左子樹。
public void deleteMin(Node node){ //刪除該節點下的最小節點
Node currentNode=node;
Node parentNode=node;
if(currentNode==null)
return;
//先定位到key,不用已經寫好的find方法而自己寫是因爲find方法是從根開始的,有些地方的用到這個函數不用從根開始
//一直往左深入,直到它的左子節點爲null,然後將其右子節點替換掉該節點(如無右節點即爲null)。
while(currentNode.left!=null){
parentNode=currentNode;
currentNode=currentNode.left;
}
parentNode.left=currentNode.right;
currentNode.right=null;
}
public void deleteMax(Node node){
//做法與min相似
Node currentNode=node;
Node parentNode=node;
if(currentNode==null)
return;
while(currentNode.right!=null){
parentNode=currentNode;
currentNode=currentNode.right;
}
parentNode.right=currentNode.left;
currentNode.left=null;
}
刪除的一般情況,上述刪除最大和最小隻是兩個特殊情況,有了上面做鋪墊下面的就好理解點,首先我們需要找到待刪除節點的左子樹上的最大值節點,或者右子樹上的最小值節點(T.Hibbard提出的一種解決方法),然後將該節點的參數值與待刪除的節點參數值進行交換,最後刪除該節點,這樣需要刪除的參數就從該二叉樹中刪除了。需要注意的是刪除後的一些引用該置爲null的一定要置爲null好讓其被回收。
public void delete(int key){
if(root==null) return;
Node currentNode=root;
Node parentNode=root;
//定位到要刪除的key 的父節點,以及當前元素
while(currentNode!=null&¤tNode.key!=key){
parentNode=currentNode;
if(currentNode.key>key){
currentNode=currentNode.left;
}else{
currentNode=currentNode.right;
}
}
System.out.println("要刪除的是:"+currentNode.key);
System.out.println("父親是:"+parentNode.key);
// 要替換刪除元素的node
Node exNode=min(currentNode.right);
System.out.println("與其替換的是:"+exNode.key);
//找到其右節點中最小節點用以替換要刪除的節點
if(parentNode.key>key){
parentNode.left=exNode;
}else{
parentNode.right=exNode;
}
deleteMin(currentNode.right); //刪除原引用
if(exNode!=null){
//將新節點中的左右子節點更新爲要刪除元素的左右節點
if(currentNode.left!=null&&exNode.key!=currentNode.left.key) //防止當子節點就是替代節點的時候引起死循環
exNode.left=currentNode.left;
if(currentNode.right!=null&&exNode.key!=currentNode.right.key)
exNode.right=currentNode.right;
}
currentNode.left=null;
currentNode.right=null;
}
中序遍歷,其他遍歷也差不多,不多講
public void inOrder(Node rootNode) { //中序遍歷
if (rootNode != null) {
inOrder(rootNode.left);
System.out.println("key: " + rootNode.key + " " + "value: " + rootNode.value);
inOrder(rootNode.right);
}
}
下面測試一下,這是測試代碼
public static void main(String[] args) {
// TODO Auto-generated method stub
Tree tree = new Tree();
tree.insert(6, 6);//插入操作,構造圖一所示的二叉樹
tree.insert(3, 3);
tree.insert(14, 14);
tree.insert(16, 16);
tree.insert(10, 10);
tree.insert(9, 9);
tree.insert(13, 13);
tree.insert(11, 11);
tree.insert(12, 12);
System.out.println("刪除前遍歷結果");
tree.inOrder(tree.root);//中序遍歷操作
System.out.println("刪除節點10之後遍歷結果");
tree.delete(10);//刪除操作
tree.inOrder(tree.root);
}
依據代碼 提示,可以自己手動輕鬆畫出這棵二叉樹,具體可自己實踐。目前實現的這個版本不可插入重複元素,其實如果要插入重複元素,只需在插入操作中找到這個元素,更新即可。做下代碼相應修改就好