二叉排序树或者是空树,或者是具有以下性质的二叉树:
(1)若左子树非空,则左子树上所有结点的关键字的值均小于它的根结点的关键字的值
(2)若右子树非空,则右子树上所有结点的关键字的值均大于等于它的根结点的关键字的值
(3)左右子树本身又是一颗二叉排序树二叉排序树的查找:
二叉排序树的查找与折半查找类似,根结点相当于查找区间的中点,左子树相当于前半子区间,右子树相当于后半子区间。查找过程为:若根结点的关键字值等于查找的关键字,成功。否则,若小于根结点的关键字值,查找左子树。若大于根结点的关键字值,查找右子树。若子树为空,查找不成功。- 二叉排序树的插入:
二叉排序树是一种动态树表,其特点是:树的结构通常不是一次生成的,而是在查找过程中,当树中不存在关键字等于给定值的结点时再进行插入。新插入的结点一定是一个新添加的叶子结点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或右孩子结点。 二叉排序树的删除:
在二叉排序树删去一个结点,分三种情况讨论:
(1). 若p结点为叶子结点,即PL(左子树)和PR(右子树)均为空树。由于删去叶子结点不破坏整棵树的结构,则可以直接删除此子结点。
(2). 若p结点只有左子树PL或右子树PR,此时只要令PL或PR直接成为其双亲结点f的左子树(当p是左子树)或右子树(当p是右子树)即可,作此修改也不破坏二叉排序树的特性。
(3). 若p结点的左子树和右子树均不空。在删去p之后,为保持其它元素之间的相对位置不变,可按中序遍历保持有序进行调整,可以有两种做法:(下面f是p的父结点)
其一是令p的左子树为f的左/右(依p是f的左子树还是右子树而定)子树,s为p左子树的最右下的结点(中序遍历的前驱),令p的右子树为s的右子树;
其二是令p的直接前驱(或直接后继)替代p,然后再从二叉排序树中删去它的直接前驱(或直接后继)-即让f的左子树(如果有的话)成为p左子树的最左下结点(如果有的话),再让f成为p的左右结点的父结点。创建二叉排序树,查找,插入,删除代码如下,当删除结点含有左右子树时,采用第二中方式。
import java.util.LinkedList;
public class BinarySortTree {
/*使用二叉链表存储二叉排序树*/
class Node{ //使用内部类定义二叉树结点数据结构
int data;
Node right;
Node left;
}
/*二叉排序树的查找*/
public Node BSTSearch(LinkedList<Node> list,int x){
/* x为待查找的数据 */
Node p,root=list.get(0);
p=root;
if(p==null) return null;
while(p!=null){
if(p.data==x) {
System.out.println("查找成功");
return p;
}else if(p.data>x){
p=p.left;
}else {
p=p.right;
}
}
System.out.println("查找失败");
return null;
}
/*二叉排序树的插入 * 在查找时,如果在二叉排序书中找不到指定元素,则插入 */
public void BSTInsert(LinkedList<Node> list,int x){
Node p,q=null; //q指向p的双亲结点
if(list.size()==0){ //list为空二叉排序树,创建根结点
p=new Node();
p.data=x;
p.left=null;
p.right=null;
list.add(p);
return;
}else{
p=list.get(0); //初始时p指向根结点
}
while(p!=null){
q=p;
if(p.data==x) {
System.out.println("元素存在,则不插入");
return;
}if(p.data<x){
p=p.right;
}else{
p=p.left;
}
}
/*查找失败,创建新结点则进行插入操作*/
{
p=new Node();
p.data=x;
p.left=null;
p.right=null;
list.add(p);
if(q.data>x) q.left=p;
else q.right=p;
}
}
/*利用二叉排序树的插入算法构造一个二叉排序树*/
public LinkedList<Node> CreatBSTree(int array[]){
LinkedList<Node> list=new LinkedList<Node>();
for(int i=0;i<array.length;i++){
BSTInsert(list,array[i]);
}
return list;
}
/*删除元素*/
public void BSTDelete(Node root,int x){
/*x为待删除的数据元素,root为根结点*/
Node q=null,p=null,s=null; //p指向删除结点,q指向删除结点的父结点,s指向删除结点的孩子结点
p=root;
/*在二叉排序树中查找数据元素x*/
while(p!=null &&p.data!=x){
q=p;
if(p.data<x)
p=p.right;
else p=p.left;
}
if (p==null) return; //如果没有找到x则不进行删除操作
s=p;
if(s.left!=null && s.right!=null){//被删除结点左右子树均不为空,查找被删除结点的中序前驱结点,并用p指向它
q=s;
p=s.left;
while(p.right!=null){
q=p;
p=p.right;
}
/*用中序前驱结点的数据覆盖被删除结点,此时中序前驱就变成了被删除结点,后面只需删除中序前驱即可*/
if(p!=s) s.data=p.data;
}
/*由于第(1),第(3)种情况都转化成了第2种情况,此时只需考虑第(2)种情况即可*/
s=(p.left!=null)?p.left:p.right; //p的左子树不为空,则s指向左子树,否则s指向右子树
if(q==null) //被删除结点为根结点,则删除后应修改根指针
root=s;
else{
if(q.left==p)
q.left=s;
else q.right=s;
}
}
/*采用中序遍历输出二叉排序树*/
public void InOrderTree(Node root){
Node p=root;
if(p==null) return;
if(p.left!=null){
InOrderTree(p.left); //递归遍历左子树
}
System.out.print(p.data+", ");
if(p.right!=null){
InOrderTree(p.right); //递归遍历右子树
}
}
public static void main(String[] args) {
int array[]=new int []{15,25,10,12,30,45,37,7,5,8,70,23};
LinkedList<Node>list=new LinkedList<Node>();
BinarySortTree bs=new BinarySortTree();
list=bs.CreatBSTree(array);
Node root=list.get(0); //root为根结点
bs.InOrderTree(root);
bs.BSTSearch(list,5);
bs.BSTDelete(root, 15);
bs.InOrderTree(root);
}
}