(九)1.1_二叉排序树
一.相关概念
二叉排序树(Binary Sort Tree)或者是一棵空树;或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
二.插入节点
算法思路:
如果插入元素小于遍历到的结点元素,就在结点左子树继续查找插入位置,如果大于则在右子树寻找插入位置,直到找到一个结点的孩子指针为空,然后就把新建结点挂在那.
我的代码实现中用到了二重指针,也就是指向结点的孩子指针的指针,这样可以用非递归方法实现构建二叉排序树
代码实现:
//二叉排序树插入元素
void InsertBST(BiTree &T,TElemType e)
{
BiTree *p=&T; //指向节点指针的指针
while((*p)!=NULL)
{
if((*p)->data>e) //如果插入元素小于遍历到的元素
p=&((*p)->lchild); //在左子树中继续查找
else if((*p)->data<e) //如果插入元素大于遍历到的元素
p=&((*p)->rchild); //在右子树中继续查找
else //已存在相同元素,则退出,不构建新结点
return;
}
(*p)=(BiTree)malloc(sizeof(BiTNode)); //找到插入位置,新建树节点
(*p)->lchild=(*p)->rchild=NULL; //左右子树赋为空
(*p)->data=e; //保存元素
}
三.删除结点
算法思路:
我们知道在算法概念上,删除一个结点,那么删除我们就要把删除节点的前驱(或者后继)放在其原位置,但是代码实现上并不是完全这样的,因为如果要删除的结点的左右子树均存在,这样操作需要多个指针指向发生改变,实现起来太复杂.所以我们要分情况
首先找到要删除的结点(利用二重指针),这个大家都知道,找到后我们有三种情况
1.如果要删除结点的左子树为空,如图也就是要删除结点7的情况,那么我们就直接删除结点7就可以,也就是使指向结点7的指针改为指向结点7右孩子(结点9),然后释放结点7的内存
2.如果要删除结点的右子树为空,如图也就是要删除结点2的情况,那么我们就直接删除结点2就可以,也就是使指向结点2的指针改为指向结点2的左孩子(结点1),然后释放结点2的内存
3.如果要删除的结点的左右子树均存在,如图也就是要删除结点3的情况,那么这个时候我们把结点3的前驱结点2的值赋值给结点3,然后删除前驱结点2,也就是使指向前驱结点2的指针指向其左子树(结点1),然后释放前驱结点2的内存
代码实现:
//删除树节点
void DeleteBST(BiTree &T,TElemType e)
{
BiTree *p=&T; //指向节点指针的指针
while((*p)!=NULL) //循环找到要被删除的节点
{
if((*p)->data>e) //如果插入元素小于遍历到的元素
p=&((*p)->lchild); //向左走
else if((*p)->data<e) //如果插入元素大于遍历到的元素
p=&((*p)->rchild); //向右走
else //找到了要删除的节点(*p)
break;
}
if((*p)==NULL) //如果排序二叉树中没有要删除的节点
return; //退出
BiTree node=(*p); //要被删除的节点node
if(node->rchild==NULL) //要被删节点node右子树不存在
(*p)=(*p)->lchild; //重接要删除节点的左子树
else if(node->lchild==NULL) //要被删节点node左子树不存在
(*p)=(*p)->rchild; //重接要删除节点的右子树
else //左右子树均存在
{ //原理:要删除的节点被赋值为其前驱节点的数据,然后删除的是前驱节点(*p)
p=&((*p)->lchild); //向左
while((*p)->rchild!=NULL) //向右走到尽头,找到被删节点node的前驱节点(*p)
p=&((*p)->rchild);
node->data=(*p)->data; //要删除的节点node的数据赋为其前驱节点的数据
node=(*p); //前驱节点(*P)才是要被删除的节点
(*p)=(*p)->lchild; //重接前驱节点(*P)的左子树,要删除节点的前驱节点(*P)必定没有右子树(由中序遍历性质可知)
}
free(node); //删除节点
}