数据结构-二叉排序树

如果需要一个满足:
支持排序性、高效插入、删除操作、高效查找的数据结构,怎么做?
先看看一些简单的数据结构:
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.排序二叉树的查找效率与树的高度有关,高度越低,查找效率越高;反之则相对较低。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章