數據結構-二叉排序樹

如果需要一個滿足:
支持排序性、高效插入、刪除操作、高效查找的數據結構,怎麼做?
先看看一些簡單的數據結構:
1)排序順序表(數組):查找可以採用折半查找算法,時間效率爲O(log2n);插入、刪除操作的時間複雜度爲O(n),數據量大時,效率太低。
2)排序單鏈表:只能採用順序查找,時間複雜度爲O(n),不能採用折半查找法;插入和刪除操作時間負責度爲O(n),雖然沒有數據移動,但因爲查找效率太低,導致插入和刪除效率都低。
3)散列表(hash table):雖然散列表的插入、刪除、查找的效率較高,但是本身不支持排序性。

下面,介紹一個數據結構:
二叉排序樹

特點:
二叉排序樹,支持排序特性,且插入、刪除、查找操作的時間複雜度可以達到O(log2n)。
因爲其排序特性,p的左孩子節點值 < p節點的值 < p右孩子節點的值,所以查找時可以在左、右孩子之間選擇一條路徑,從而將查找範圍縮小了一半,一次查找只需要經過從根節點到某節點的一條路勁就可以獲得查找結果,比較次數最多等於二叉樹的高度,查找時間複雜度爲O(log2n),遠低於O(n)。
注意:二叉排序樹,使用中根次序遍歷,可以得到從小到大的元素值序列。

code:(二叉排序樹的插入實現)
public void insert(T x){
// 判空等操作...
BinaryNode<T> p = this.root,parent = null;
while(null != p){
parent = p;
int compareResult = x.compareTo(p.data);
if(compareResult == 0)
return; // 不插入關鍵字重複的數據
if(compareResult < 0)
p = p.left;
else
p = p.right;
}
p = new BinaryNode<T>(x); // 建立葉子節點p
if(x.compareTo(parent.data) < 0)
parent.left = p; // 小值放左邊
else 
parent.right = p; // 大值放右邊
}


code:(二叉排序樹的查找實現)
public BinaryNode<T> search(T key){
// 判空...
BinaryNode<T> p = this.root;
while(null != p){
int compareResult = p.data.compareTo(key);
if(compareResult == 0)
return p;
if(compareReuslt < 0)
p = p.left;
else
p = p.right;
}
return p;
}
排序二叉樹的刪除稍微複雜一些,有可能出現三種不同的情況:
1)待刪除的節點p爲葉子節點,則刪除p節點,並設置其parent的left或right節點爲null
2)待刪除的節點爲1度節點,則刪除p節點,並用p的孩子節點作爲其parent的孩子節點
a若p是parent的左孩子且p有左孩子,設置parent的left指向p的左孩子
b若p是parent的左孩子且有右孩子,設置parent的left指向p的右孩子
c若p是parent的右孩子且p有左孩子,設置parent的right指向p的左孩子
d若p是parent的右孩子且有右孩子,設置parent的right指向p的右孩子
3)待刪除節點爲2度節點,則不直接刪除p,而是先用p節點在中根次序下的後繼節點insucc值代替p節點值,再刪除insucc節點。這樣可以將2度節點的刪除轉變爲1度節點的刪除問題,最小程度的調整二叉樹的結構。
code:刪除並返回被刪除的節點
private BinaryNode<T> remove(T x, BinaryNode<T> p, BinaryNode<T> parent){
if(null == p)
return null;
if(x.compareTo(p.data) < 0)
return remove(x, p.left, p);
if(x.compareTo(p.data) > 0)
return remove(x, p.right, p);
// 找到待刪除節點,若爲2度節點
if(null != p.left && null != p.right){
BinaryNode<T> insucc = p.right;
// 找到p的中根次序下的後繼節點(就是p右子樹下的最左邊的葉子節點)
// 這個葉子節點肯定比p大,因爲在p的右子樹下,但卻是右子樹中最小的一個(最左)
while(null != insucc.left)
insucc = insucc.left;
p.data = insucc.data; // 將insucc的值拷貝給p節點
// 現在刪除insucc節點即可轉變爲1度節點(或葉子節點)的刪除
return remove(p.data, p.right, p); // 從p的右子樹開始檢索刪除insucc
}
if(null == parent){ // 即查找到的是根節點
if(null != p.left)
root = p.left;
else
roo = p.right;
return p;
}
if(p == parent.left) // 刪除一度節點或葉子節點,p是parent的左孩子
if(null != p.left)
parent.left = p.left;
else
parent.left = p.right;
else
if(p.left != null)
parent.right = p.left;
else
parent.right = p.right;
return p;
}
注意:由於插入和刪除的規則不同,若刪除一個非葉子節點,再將其立即插入,所得到的排序二叉樹結構不一定與原先的相同。

排序二叉樹,來個總結:
1.插入、刪除、查找的時間複雜度爲O(log2n)
2.先根次序遍歷,可以得到權值的從小到大的排列序列
3.刪除算法要分三種情況:被刪除的爲葉子節點、1度節點、2度節點,其中2度節點的刪除應該轉變爲一度節點的刪除,方法就是找到被刪除節點p在中根次序下的後繼節點insucc,將insucc節點的值賦給p,刪除insucc節點即可;1度節點的刪除,將p節點的子節點(無論左、右),放在p的位置,移除p即可。
4.排序二叉樹的查找效率與樹的高度有關,高度越低,查找效率越高;反之則相對較低。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章