基於數組的二叉查找樹 Binary Search Tree (Java實現)

二叉查找樹

     二叉查找樹是一種支持動態查詢的數據結構,所謂動態查尋結構:即在當數據集合內容發生改變時,集合內數據的排列組合不用從新構建。這樣的數據結構在查詢時須要不斷變更的場景中是很是高效的,二叉查找樹就是其中一個,而且它是SBT,AVL,紅黑樹的基礎,一直有興趣想要研究下。原理就不介紹了,可參考這個 。今天花了半天時間,本身完成了一個基於數組的Java實現。java

實現的方法:INSERT、SEACH、DELETE

      先簡單說明下這幾個方法的實現原理:node

SEARCH:

      查詢方法的思路很簡單,和二分查找類似,即從根節點開始查找,若待查找元素a小於根節點,則在該節點的左子樹中繼續查找,大於則去右子樹中,等於即是找到了。

INSERT:

      插入方法,也是一個查詢的過程,若根節點爲空,則直接設置成跟節點,不然依以下方式與節點比較: 若小於節點值,將元素插入其左子樹,若大於該節點值插入其右子樹。若發現相等的則退出,算是排重。

DELETE:

      刪除操做相對比較複雜,若要刪除某一個節點 Z ,可能碰見三種狀況:
     一.    節點 Z 不包含任何子樹;此時可直接刪除,不影響數據的結構。
     二.    節點 Z  只有一個子子樹,左子樹或右子樹;此時只需將 Z 的惟一的那個子樹的根節點指向 Z 的父節點便可,此操做也不影響數據的。(但因爲我是基於數組的,指針式經過數組的位置表示,因此某個節點指向變了,其全部子節點在數組中的位置都要變,不過好在這種變化是有規律的,我已在程序中註明。)
     三.     節點 Z 的左子樹和右子樹同時存在。這種狀況最爲複雜,首先找到 Z 左子樹中值最大的節點,記爲 H ,將代替 Z 在樹種的位置,而後再以遞歸的方式刪除節點 H.

下面是我本身實現的代碼,如有錯誤或可優化的地方,還望各位看官及時指出,我好更正。數組

package com.mycode.structures;

import java.util.Collection;

/**
 * 基於數組二叉查找樹實現
 * @author Breath_L
 * @param <T>
 */
public class BSTree<T extends Comparable<T>> {
	private static final int DEFAULT_SIZE = 10;
	private T[] data;
	private Integer count;
	
	public Integer getCount(){
		return count;
	}
	
	public BSTree(int size){
		data = (T[]) new Object[size];
		count = 0;
	}
	
	public BSTree(){
		data = (T[]) new Object[DEFAULT_SIZE];
		count = 0;
	}
	
	/**
	 * 擴充容量
	 */
	private void expandSize(){
		T[] newDate = (T[]) new Object[data.length * 2];
		System.arraycopy(data, 0, newDate, 0, data.length);
		data = newDate;		
	}
	
	/**
	 * 判斷二叉樹中是否包含元素 one
	 * @param one
	 * @return 若找到了,返回該元素在數組中的位置,不然返回-1
	 */
	public int search(T one){
		if(data[0] == null){
			System.out.println("==> This BSTree is Empty!");
			return -1;
		}else{
			int index = 0;
			while(index < data.length && data[index] != null){
				int f = one.compareTo(data[index]);
				if(f == 0){                    //找到了返回其位置
					return index;
				}else if(f < 0){
					index = 2 * index + 1;
				}else{
					index = 2 * index + 2;
				}
			}
			return -1;
		}
	}
	
	/**
	 * 添加元素
	 * @param t
	 */
	public void add(T t){
		if(data[0] == null){
			data[0] = t;
			count++;
			return;
		}else{
			int index = 0;
			while(index < data.length){
				if(t.compareTo(data[index])<0){
					int left = 2 * index + 1;
					if(left >= data.length)
						expandSize();					
					if(data[left] == null){
						data[left] = t;
						break;
					}else{
						index = left;
					}
				}else if(t.compareTo(data[index]) > 0){
					int right = 2 * index + 2;
					if(right >= data.length)
						expandSize();
					if(data[right] == null){
						data[right] = t;
						break;
					}else{
						index = right;
					}
				}else{                        // 相同元素不處理,算是排重了
					break;
				}				
			}
		}
		count++;
	}
	
	public void addAll(Collection<T> all){
		for (T t:all){
			add(t);
		}
	}
	public void addAll(T[] all){
		for (T t:all){
			add(t);
		}
	}
	
	public void delete(T del){
		int del_index = this.search(del);
		if(del_index != -1){                    //等於-1 表示沒有,便不作處理
			real_delete(del_index);
		}
	}
	
	/**
	 * 刪除某個節點
	 * @param index:該節點在數組中的位置
	 * @return 
	 */
	private void real_delete(int index){
		int lc = 2*index + 1;
		int rc = 2*index + 2;
		if(data[lc] != null && data[rc] != null){        // 左子樹、右子樹同時存在的狀況
			int left_max_child = findLeftMaxChild(index);
			data[index] = data[left_max_child];          //刪除節點
			real_delete(left_max_child);                 //遞歸刪除左子樹中值最大的節點
		}else if(data[lc] == null && data[rc] == null){  // 都沒有則直接刪除
			data[index] = null;
		}else{ 
			if(data[lc] != null){
				replaceNodeWithChild(lc, lc-index);
			}else{
				replaceNodeWithChild(rc, rc-index);
			}
		}
	}
	
	/**
	 * 尋找某個節點的左子樹中最大節點
	 * @param index 某個節點的位置
	 * @return 最大節點位置
	 */
	private int findLeftMaxChild(int index){
		int left = 2*index +1;
		int bigger = 2*left + 2;
		while( bigger < data.length && data[bigger] != null){			
			left = bigger;
			bigger = 2 * bigger + 2;			
		}
		return left;
	}
	
	/**
	 * 若子節點C替換了其父節點P,則C的全部子節點都須要被移動(因爲數組的緣由),distance爲C和P在數組中位置之差。
	 * 其全部子節點在數組中移動的距離爲 distance*(2^x),x爲這些子節點與節點C的距離(相鄰節點距離爲1);
	 * @param node
	 * @param distance
	 */
	private void replaceNodeWithChild(int node, int distance){
		int left = 2*node+1;
		int right = 2*node+2;
		int current_distance = distance*2;                   //每次遞歸距離*2 
		if(data[left] != null){
			data[left - current_distance] = data[left];
			replaceNodeWithChild(left,current_distance);     //遞歸遍歷下個節點
		}
		if(data[right] != null){
			data[right - current_distance] = data[right];
			replaceNodeWithChild(right,current_distance);
		}
	}
}

原創博客,轉載請註明 http://my.oschina.net/BreathL/blog/54734
相關文章
相關標籤/搜索