二叉排序树是具有以下性质的二叉树:
1)如果左子树不为空,那么左子树的值均小于根节点的值,如果右子树不为空,那么右子树的值均大于根节点的值;
2)左右子树也分别是二叉排序树;
又称为二叉搜索树,二叉查找树。
二叉树的基本运算包括查找,插入和删除,其中以删除最难,这里就不介绍查找。
二叉树的插入:
需要认识到,二叉排序树插入的节点一定是作为叶子节点添加进去的;
//二叉排序树的插入
public static Node InsertNode(Node t,KeyType k)
{
Node f,p,s;//声明几个节点,s是新插入的点,p是s的双亲,f是p的双亲
p = t;
while(p)
{
if(p.data == k)
{
return t;
System.out.println("存在k,不需要插入");
}
else
{
f = p;
if(p.data<k) p = p.rchild;
else p =p.lchild;
}
}//找到要插入的点的双亲p
s = new Node(k);
s.lchild=null;
s.rchild=null;//新建节点
if(t==null) t=s;
else if(p.data<k) p.rchild = s;
else p.lchild = s;//根据大小插入s
return t;
}
在二叉排序树中删除一个节点:
在二叉排序树中删除一个节点,首先要查找,如果没有这个点,查找失败,否则,根据下面的方法进行删除。
设待删除节点指针p,双亲节点为f,分三种情况:
1)p为叶子节点,那么删除此节点不会影响排序树的特性,直接删除,只需要把该节点的双亲节点相应位置置为null
2)p节点是单支节点,即p节点只有左子树Pl或者右子树Pr,此时只需要用Pl或Pr替代p的位置即可
3)p节点是双支节点,既有左子树,又有右子树,此时要按中序遍历保持排序树特性来进行删除,有两种方法:
1,用p的右子树pr代替以p为根的子树,即以pr为根的子树上升,再根据中序遍历序列(中序遍历保持不变),调整p的原左子树pl,
让他作为以pr为根的子树中序遍历时第一个节点的左子树(保证中序遍历是pl依然是在pr前面);
2,用p的直接直接后继(或直接前驱)代替p节点,这个节点只能是叶子或者单支节点(肯定存在),再按1)或2)删除p;
从上述的调整方案可以看到,按3)中的1方法,排序树的长度是可能会增加的,而按照2)方法排序数的高度肯定不会增加,
还有可能减小。
下面按2)方法实现一个节点的删除;
设待删除节点指针p,双亲节点为f,分三种情况:
1)p为叶子节点,那么删除此节点不会影响排序树的特性,直接删除,只需要把该节点的双亲节点相应位置置为null
2)p节点是单支节点,即p节点只有左子树Pl或者右子树Pr,此时只需要用Pl或Pr替代p的位置即可
3)p节点是双支节点,既有左子树,又有右子树,此时要按中序遍历保持排序树特性来进行删除,有两种方法:
1,用p的右子树pr代替以p为根的子树,即以pr为根的子树上升,再根据中序遍历序列(中序遍历保持不变),调整p的原左子树pl,
让他作为以pr为根的子树中序遍历时第一个节点的左子树(保证中序遍历是pl依然是在pr前面);
2,用p的直接直接后继(或直接前驱)代替p节点,这个节点只能是叶子或者单支节点(肯定存在),再按1)或2)删除p;
从上述的调整方案可以看到,按3)中的1方法,排序树的长度是可能会增加的,而按照2)方法排序数的高度肯定不会增加,
还有可能减小。
下面按2)方法实现一个节点的删除;
public static Node DeleteNode(Node <DataType> t,KeyType k)
//在根节点为t的二叉排序树中删除元素为k的节点,并返回根节点
{
//声明几个节点,p是待删除节点,f是p的双亲节点,s是p的中序遍历中的直接后继节点,pre是s的双亲节点
Node <DataType> f,s,p,r,pre;
//先搜索待删节点
p = t;
while(p && p.data!=k)
{
f = p;
if(p.data<k)
p = p.lchild;
else
p = p.rchile;
}
if(p==null)
{
System.out.println("不存在此节点,删除失败!");
return t;
}
if(p.lchild && p.rchild)
{
pre = p;
s = p.rchild;
while(s.lchild!=null)
{
pre = s;
s = s.lchild;
}//查找到p点中序遍历的直接后继节点s
p.data = s.data; // s点取代待删p点
r = s.rchild; // s的右子树上升(如果存在)
if(pre == p) pre.rchild = r;
else pre.lchild = r;
}
else //情况1)和2)
{
if(p.lchild == null) r = p.lchild;
else if(p.rchild == null) r = p.rchild;
if(f == null)
{
return t;//待删的是根节点
}
else if(f.lchild==p) f.lchild = r;
else if(f.rchild==p) f.rchild ==r;
}
return t;
}