目錄
二叉查找樹的特點
1. 如果左樹不爲空,那麼左樹上所有結點的值小於根結點的值
2. 如果右樹不爲空,那麼右樹上所有節點的值大於根結點的值
3. 左樹和右樹均是二叉查找樹,並且樹上的結點的值都不相同
二叉查找樹的操作
插入操作
將9插入到a圖的二叉樹
1> 9大於4,插入到4的右樹
2> 9大於5,插入到5的右樹,接着9還是大於6,將9插入到6的右樹
3> 9大於7,插入到7的右樹,9大於8,插入到8的右樹
在此過程中,動態增加數據都會加在原有數據的葉子節點上。
刪除操作
1. 如果這個結點的左樹存在,右樹不存在
例如
當刪除的節點在左樹:
例如,刪除a圖的節點3,只需要將結點4和結點1構成關係。表示爲 parent.left = node.left
當刪除的結點在右樹
例如,刪除c圖的結點2,只需要將結點1和結點3構成關係,表爲parent.right = node.rigtht;
2. 如果這個結點的右樹存在,左樹不存在,和上面類似。
3. 當刪除的結點,左樹和右樹都存在。
如果刪除結點5,需要一個結點能夠替換5,結點4和結點6都能夠滿足要求。
如何找到這個結點替換刪除的結點?
當前結點的左結點的走右樹,一直找到最右結點 或者是 當前結點的右結點,走左樹找到的最左結點。
源碼中展示的刪除方法,是按照 "當前結點的左結點,走右樹,找到最右結點" 的方式,編碼的。
二叉查找樹的效率分析
很顯然,在a,b兩圖的二叉查找樹結構中查找一個數據,並不需要遍歷全部的節點元素,查找效率確實提高了。但是有一個很嚴重的問題:我們在a圖中查找8需要比較5次數據,而在B圖中只需要比較3次。更爲嚴重的是:如果按有序序列[1 2 3 4 5 6 7 8]建立一顆二叉查找樹,整棵樹就退化成了一個線性結構(如c輸入圖:單支樹),此時查找8需要比較8次數據,和順序查找沒有什麼不同。
最壞的情況,時間複雜度算法是O(N),最好的情況和折半查找的時間複雜度相同,爲O(log2 (N))
源碼
package net.lingala.zip4j.test;
import java.util.ArrayList;
import com.sun.corba.se.impl.logging.InterceptorsSystemException;
/**
* 二叉樹節點結構
* @author heartraid
*/
class BSTNode<E extends Comparable<E>>{
/**結點關鍵字*/
E key=null;
/**直接父親結點*/
BSTNode<E> parent=null;
/**結點左子樹的根節點*/
BSTNode<E> lchild=null;
/**結點右子樹的根節點*/
BSTNode<E> rchild=null;
BSTNode(E k){
this.key=k;
}
}
/**
* 二叉查找樹 Binary Search Tree(BST)
* @author heartraid
*
*/
public class BST<E extends Comparable<E>> {
/**樹根*/
private BSTNode<E> root=null;
public BST(){
}
/**
* BST 查詢關鍵字
* @param key 關鍵字
* @return 查詢成功/true, 查詢失敗/false
*/
public boolean search(E key){
System.out.print("搜索關鍵字["+key+"]:");
if(key==null||root==null){
System.out.println("搜索失敗");
return false;
}
else{
System.out.print("搜索路徑[");
if(searchBST(root,key)==null){
return false;
}
else return true;
}
}
/**
* BST插入關鍵字
* @param key 關鍵字
* @return 插入成功/true, 插入失敗/false
*/
public boolean insert(E key){
System.out.print("插入關鍵字["+key+"]:");
if(key==null) return false;
if(root==null){
System.out.println("插入到樹根。");
root=new BSTNode<E>(key);
return true;
}
else{
System.out.print("搜索路徑[");
return insertBST(root,key);
}
}
public boolean delete(E key){
System.out.print("刪除關鍵字["+key+"]:");
if(key==null||root==null){
System.out.println("刪除失敗");
return false;
}
else{
System.out.print("搜索路徑[");
//定位到樹中待刪除的結點
BSTNode<E> nodeDel=searchBST(root,key);
if(nodeDel==null){
return false;
}
else{
//nodeDel的右子樹爲空,則只需要重接它的左子樹
if(nodeDel.rchild==null){
BSTNode<E> parent=nodeDel.parent;
if(parent.lchild.key.compareTo(nodeDel.key)==0)
parent.lchild=nodeDel.lchild;
else
parent.rchild=nodeDel.lchild;
}
//左子樹爲空,則重接它的右子樹
else if(nodeDel.lchild==null){
BSTNode<E> parent=nodeDel.parent;
if(parent.lchild.key.compareTo(nodeDel.key)==0)
parent.lchild=nodeDel.rchild;
else
parent.rchild=nodeDel.rchild;
}
//左右子樹均不空
else{
BSTNode<E> q=nodeDel;
//先找nodeDel的左結點s
BSTNode<E> s=nodeDel.lchild;
//然後再向s的右盡頭定位(這個結點將替代nodeDel),其中q一直定位在s的直接父親結點
while(s.rchild!=null){
q=s;
s=s.rchild;
}
//換掉nodeDel的關鍵字爲s的關鍵字
nodeDel.key=s.key;
//重新設置s的左子樹
if(q!=nodeDel)
q.rchild=s.lchild;
else
q.lchild=s.lchild;
}
return true;
}
}
}
/**
* 遞歸查找關鍵子
* @param node 樹結點
* @param key 關鍵字
* @return 查找成功,返回該結點,否則返回null。
*/
private BSTNode<E> searchBST(BSTNode<E> node, E key){
if(node==null){
System.out.println("]. 搜索失敗");
return null;
}
System.out.print(node.key+" —>");
//搜索到關鍵字
if(node.key.compareTo(key)==0){
System.out.println("]. 搜索成功");
return node;
}
//在左子樹搜索
else if(node.key.compareTo(key)>0){
return searchBST(node.lchild,key);
}
//在右子樹搜索
else{
return searchBST(node.rchild,key);
}
}
/**
* 遞歸插入關鍵字
* @param node 樹結點
* @param key 樹關鍵字
* @return true/插入成功,false/插入失敗
*/
private boolean insertBST(BSTNode<E> node, E key){
System.out.print(node.key+" —>");
//在原樹中找到相同的關鍵字,無需插入。
if(node.key.compareTo(key)==0)
{
System.out.println("]. 搜索有相同關鍵字,插入失敗");
return false;
}
else{
//搜索node的左子樹
if(node.key.compareTo(key)>0){
//如果當前node的左子樹爲空,則將新結點key node插入到左孩子處
if(node.lchild==null) {
System.out.println("]. 插入到"+node.key+"的左孩子");
BSTNode<E> newNode=new BSTNode<E>(key);
node.lchild=newNode;
newNode.parent=node;
return true;
}
//如果當前node的左子樹存在,則繼續遞歸左子樹
else return insertBST(node.lchild, key);
}
//搜索node的右子樹
else{
if(node.rchild==null){
System.out.println("]. 插入到"+node.key+"的右孩子");
BSTNode<E> newNode=new BSTNode<E>(key);
node.rchild=newNode;
newNode.parent=node;
return true;
}
else return insertBST(node.rchild,key);
}
}
}
/**
* 得到BST根節點
* @return BST根節點f
*/
public BSTNode<E> getRoot(){
return this.root;
}
/**
* 非遞歸中序遍歷BST
*/
public void InOrderTraverse(){
if(root==null)
return;
BSTNode<E> node=root;
ArrayList<BSTNode<E>> stack=new ArrayList<BSTNode<E>>();
stack.add(node);
while(!stack.isEmpty()){
while(node.lchild!=null){
node=node.lchild;
stack.add(node);
}
if(!stack.isEmpty()){
BSTNode<E> topNode=stack.get(stack.size()-1);
System.out.print(topNode.key+" ");
stack.remove(stack.size()-1);
if(topNode.rchild!=null){
node=topNode.rchild;
stack.add(node);
}
}
}
}
/**
* 測試
*/
public static void main(String[] args) {
BST<Integer> tree=new BST<Integer>();
tree.insert(new Integer(100));
tree.insert(new Integer(52));
tree.insert(new Integer(166));
tree.insert(new Integer(74));
tree.insert(new Integer(11));
tree.insert(new Integer(13));
tree.insert(new Integer(66));
tree.insert(new Integer(121));
tree.insert(new Integer(10));
tree.search(new Integer(11));
tree.InOrderTraverse();
tree.delete(new Integer(11));
tree.InOrderTraverse();
}
}
參考博客:https://blog.csdn.net/zxnsirius/article/details/52131433?utm_source=blogxgwz0 (關於刪除結點的查找方法)
http://hxraid.iteye.com/blog/609312(二叉查找樹介紹)