排序二叉树,搜索二叉树,查找二叉树都是一个意思,只是叫法不同而已。下面的文章中我们统称为排序二叉树。本文主要是针对高中信息学,因此其中不涉及到指针,所有需要用指针的地方都直接使用数组进行模拟。
排序二叉树定义:
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
从定义中我们可以知道:左<根<右;因此使用中序遍历排序二叉树一定是一个递增序列。
节点定义:
struct node{
int data;//节点值的类型,以整型为例
int left;//左儿子数组下标
int right;//右儿子数组下标
int father;//该节点的父节点数组下标
int freq;//该节点的值出现的频率
};
node arr[105]={0};//定义一个大小为105的数组。可以装105个元素,
int root=1;//根节点对应的数组下标
建树:
排序二叉树的建立并不难,因为建树的过程就是将值一个一个插入到树中去的过程。例如这里使用随机函数生成100个随机值,然后依次插入到排序二叉树中就完成了建树。
srand(unsigned(time(NULL)));
int x;
for(int i=1;i<101;i++){
arr[i].data=rand()%10000;
insert(root,i);//调用插入函数
}
插入元素:
向二叉树中插入元素需要通过将待插入的值和某个节点对比,决定是插入到这个节点的左边还是右边。例如要想下面的二叉树中插入21,首先21和3相比,比3大则一定在3的右边,然后21和8比,又大;则到8的右边,而8的右边没有,那么将21插入作为8的右儿子

实现代码:
void insert(int index,int i){//i为要插入的值的下标,index为插入过程中与arr[i]相比的值的数组下标
int y=arr[index].data;
int x=arr[i].data;
if(x==y){//如果找到了一个相同的,就将频率加1
arr[index].freq++;
return ;
}
if(arr[index].left!=0&&x<y)//如果x<y并且y有左儿子,就到左边去插入
insert(arr[index].left,i);
else if(arr[index].right!=0&&x>y)//如果x<y并且y有右儿子,就到右边去插入
insert(arr[index].right,i);
else if(arr[index].left==0&&x<y){//如果x<y并且y没有左儿子,就插入到y的左边
arr[index].left=i;
arr[index].freq=1;
arr[i].father=index;
return;
}
else if(arr[index].right==0&&x>y){//如果x>y并且y没有右儿子,就插入到y的右边
arr[index].right=i;
arr[i].father=index;
arr[index].freq=1;
return;
}
}
查找元素:
查找元素很容易,只有两种结果:找到和找不到。找到就是其中某个值和要查找的值相等,找不到就是把树都走完了,但是还是没有相等的。查找和插入很相似,同样是通过比较决定到左还是右边去查找。
比如:查找7
1.和12相比小------左
2.和6相比大--------右
3.和8相比小--------左
4.和7相比等--------找到
若是在第3步,8不存在左子树,那么返回找不到
实现代码:
int find(int r,int da){//da表示要查找的数据,r表示一路比较的数据的下标
int dar=arr[r].data;
int dl=arr[r].left;
int dr=arr[r].right;
if(da==dar)//相等,返回数据下表
return r;
else if(da<dar&&dl)//小于,且数据存在左子树,到左边去找
find(dl,da);
else if(da>dar&&dr)//大于,且数据存在右子树,到右边去找
find(dr,da);
else{//否则返回-1表示没有找到
//cout<<"not find!"<<endl;
return -1;
}
}
删除元素:
删除元素是排序二叉树中最难的操作,比较繁杂。因为删除一个元素有几种情况:
1.该元素是叶子结点
2.该元素只有左子树
3.该元素只有右子树
4.该元素左右子树都存在
这还没完,我们删除的时候还要判断我们要删除的元素到底是他的父元素的左儿子还是右儿子。以及要判断删除的节点是不是根节点。
其实对于前三种情况都简单;关键是第4种两个子树都存在的情况,这里删除操作有几种方法:
1.找到右子树中最小的值min,然后用min替换要删除的元素,最后删掉min。
2.找到左子树中最大值max,然后用max替换要删除的元素,最后删掉max
3.找到左子树中的最大值max,然后将右子树作为max的右子树,最后将元素删除并让该元素的左儿子上位。
4.和3一样,找到右子树min,然后接上去。。。。。。
这里可以看到这里的四种方式前两种操作起来要容易一些,可以省一些事。
例如我要删除图中的6
1.用7覆盖6,然后删除7
2.5覆盖6,删除5
3.将3作为12的左儿子,然后把6的右儿子连到5的右边
代码实现
void del(int index,int num){
int x=find(index,num);
if(x<0)return;
int l=arr[x].left;
int r=arr[x].right;
int f=arr[x].father;
if(l!=0&&r!=0){
int temp=r;
while(arr[temp].left)temp=arr[temp].left;//找到右子树中的最小节点
arr[x].data=arr[temp].data;//将最小节点和当前需要删除的节点进行替换
arr[x].freq=arr[temp].freq;
if(temp!=r)//temp!=r则去掉最小节点的父节点的left
arr[arr[temp].father].left=0;
else{//temp==r表示右子节点不存在左树,则直接用右子节点的right替换待删除的right
arr[x].right=arr[temp].right;
arr[arr[x].right].father=x;
}
arr[temp].father=0;
}
else{
int temp=0;
if(l==0)
temp=r;
else
temp=l;
if(arr[f].left==x)
arr[f].left=temp;
else
arr[f].right=temp;
arr[x].father=0;
if(temp)
arr[temp].father=f;
if(x==root)
root=temp;
}
}
运行截图:使用中序遍历后是一个上升序列