一、二叉排序树(二叉查找树)的概念
(1)若左子树非空,则左子树上所有结点的值均小于根节点的值
(2)若右子树非空,则右子树上所有结点的值均大于根节点的值
(3)左右子树分别也是一棵二叉排序树
tip:可以是一棵空树
二、二叉排序树的判别
(1)因为二叉排序树的中序遍历是一个有序递增序列,可以对已经建立的二叉树进行中序遍历,如果满足则判断是
三、二叉排序树的创建(creat、insert)
树结点的结构体:
struct tree{
int data;
struct tree* lchild;
struct tree* rchild;
};
//递归创建结点
void Creat(int a,tree* &T){
if(T==NULL){
T=new tree;
T->data=a;
T->lchild=NULL;
T->rchild=NULL;
}
else if(a>T->data){
Creat(a,T->rchild);
}
else{
Creat(a,T->lchild);
}
}
//传入数组,一次性插入
void Insert(tree* &T,int A[],int len){
for(int i=0;i<=len;i++){
Creat(A[i],T);
}
}
四、二叉排序树的插入
//查找指定结点(输出当前结点是否存在),如果没有就插入
void find(tree* &T,int a){
tree* K=T; //T指针指向二叉排序树的根节点,K为工作指针
tree* pre; //pre指向当前工作指针的上一个结点,用于插入确定插入位置
while(K!=NULL&&a!=K->data){
if(a>K->data){
pre=K;
K=K->rchild;
}else{
pre=K;
K=K->lchild;
}
}
if(K==NULL){
tree* P; //工作指针
P=new tree;
P->data=a;
if(P->data>pre->data){
pre->rchild=P;
P->lchild=NULL;
P->rchild=NULL;
}
else{
pre->lchild=P;
P->lchild=NULL;
P->rchild=NULL;
}
cout<<"不存在,已插入 "<<a<<" 这个结点"<<endl;
}else{
cout<<"存在"<<endl;
}
}
五、二插排序树的删除
//删除某一结点,若不存在则提示
//①当该结点是叶子结点时,直接删除
//②当该结点有一个左孩子或者一个右孩子时,让其孩子结点代替他的位置
//③当左右孩子都存在时找中序遍历的下一个(或上一个)结点代替其位置
void delect(tree* &T,int a){
//首先找到要删除的结点
tree* Pre;
tree* P=T; //定义工作指针
while(P!=NULL&&a!=P->data){ //这两个判定条件不能颠倒
if(a>P->data){
Pre=P;
P=P->rchild;
}else{
Pre=P;
P=P->lchild;
}
}
if(P==NULL){
cout<<"要删除的结点不存在"<<endl;
}else{
// ①当该结点是叶子结点时,直接删除
if(P->lchild==NULL&&P->rchild==NULL){
if(P->data>Pre->data){
Pre->rchild=NULL;
}else{
Pre->lchild=NULL;
}
cout<<"已删除 "<<a<<endl;
}
//②当该结点有一个左孩子或者一个右孩子时,让其孩子结点代替他的位置
if((P->lchild!=NULL&&P->rchild==NULL)||(P->rchild!=NULL&&P->lchild==NULL)){
if(P->data>Pre->data){
if(P->lchild!=NULL){
Pre->rchild=P->lchild;
}else{
Pre->rchild=P->rchild;
}
}
if(P->data<Pre->data){
if(P->lchild!=NULL){
Pre->lchild=P->lchild;
}else{
Pre->lchild=P->rchild;
}
}
cout<<"已删除 "<<a<<endl;
}
//③当左右孩子都存在时找中序遍历的下一个(或上一个结点)结点代替其位置 ,这里采用中序遍历上一个结点来代替
if(P->lchild!=NULL&&P->rchild!=NULL){
tree* Q=P->lchild; //工作指针
tree* Pre2; //用于记录最终遍历到的结点的前序结点,用于后期连接
while(Q->rchild){
Pre2=Q;
Q=Q->rchild;
}
P->data=Q->data;
if(P!=Pre2){
Pre2->rchild=Q->lchild;
}else{
Pre2->lchild=Q->lchild;
}
delete(Q);
cout<<"已删除 "<<a<<endl;
}
}
}
这里对第三种情况作一些补充:
注意:这里采用的是找左子树的最右下结点来代替要删除的结点(对于王道书上的用中序遍历的下一个结点的方法是一样的,后面也会附上代码)
第一步:找到左子树的最右下结点(这里用到了一个工作指针Q)
无非只有下面几种情况:
由此我们可以王道书上的版本,这里只给代码,分析和上面类似
if(P->lchild!=NULL&&P->rchild!=NULL){
tree* Q=P->rchild;
tree* Pre2;
while(Q->lchild){
Pre2=Q;
Q=Q->lchild;
}
P->data=Q->data;
if(P!=Pre2){
Pre2->lchild=Q->rchild;
}else{
Pre2->rchild=Q->rchild;
}
delete(Q);
}
六、完整代码(可以运行,两种方法都有)
#include<iostream>
using namespace std;
struct tree{
int data;
struct tree* lchild;
struct tree* rchild;
};
//建立创建,传入一个完整的数组
void Creat(int a,tree* &T){
if(T==NULL){
T=new tree;
T->data=a;
T->lchild=NULL;
T->rchild=NULL;
}
else if(a>T->data){
Creat(a,T->rchild);
}
else{
Creat(a,T->lchild);
}
}
//传入数组,一次性插入
void Insert(tree* &T,int A[],int len){
for(int i=0;i<=len;i++){
Creat(A[i],T);
}
}
//中序遍历
void midorder(tree* T){
if(T!=NULL){
midorder(T->lchild);
cout<<T->data<<" ";
midorder(T->rchild);
}
}
//判断是否是二叉排序树
//对树进行中序遍历,然后判断是否有序递增
//查找指定结点(输出当前结点是否存在),如果没有就插入
void find(tree* &T,int a){
tree* K=T; //T指针指向二叉排序树的根节点,K为工作指针
tree* pre; //pre指向当前工作指针的上一个结点,用于插入确定插入位置
while(K!=NULL&&a!=K->data){
if(a>K->data){
pre=K;
K=K->rchild;
}else{
pre=K;
K=K->lchild;
}
}
if(K==NULL){
tree* P; //工作指针
P=new tree;
P->data=a;
if(P->data>pre->data){
pre->rchild=P;
P->lchild=NULL;
P->rchild=NULL;
}
else{
pre->lchild=P;
P->lchild=NULL;
P->rchild=NULL;
}
cout<<"不存在,已插入 "<<a<<" 这个结点"<<endl;
}else{
cout<<"存在"<<endl;
}
}
//删除某一结点,若不存在则提示
//①当该结点是叶子结点时,直接删除
//②当该结点有一个左孩子或者一个右孩子时,让其孩子结点代替他的位置
//③当左右孩子都存在时找中序遍历的下一个(或上一个)结点代替其位置
void delect(tree* &T,int a){
//首先找到要删除的结点
tree* Pre;
tree* P=T; //定义工作指针
while(P!=NULL&&a!=P->data){ //这两个判定条件不能颠倒
if(a>P->data){
Pre=P;
P=P->rchild;
}else{
Pre=P;
P=P->lchild;
}
}
if(P==NULL){
cout<<"要删除的结点不存在"<<endl;
}else{
// ①当该结点是叶子结点时,直接删除
if(P->lchild==NULL&&P->rchild==NULL){
if(P->data>Pre->data){
Pre->rchild=NULL;
}else{
Pre->lchild=NULL;
}
cout<<"已删除 "<<a<<endl;
}
//②当该结点有一个左孩子或者一个右孩子时,让其孩子结点代替他的位置
if((P->lchild!=NULL&&P->rchild==NULL)||(P->rchild!=NULL&&P->lchild==NULL)){
if(P->data>Pre->data){
if(P->lchild!=NULL){
Pre->rchild=P->lchild;
}else{
Pre->rchild=P->rchild;
}
}
if(P->data<Pre->data){
if(P->lchild!=NULL){
Pre->lchild=P->lchild;
}else{
Pre->lchild=P->rchild;
}
}
cout<<"已删除 "<<a<<endl;
}
//③当左右孩子都存在时找中序遍历的下一个(或上一个结点)结点代替其位置 ,这里采用中序遍历上一个结点来代替
// if(P->lchild!=NULL&&P->rchild!=NULL){
// tree* Q=P->lchild; //工作指针
// tree* Pre2; //用于记录最终遍历到的结点的前序结点,用于后期连接
// while(Q->rchild){
// Pre2=Q;
// Q=Q->rchild;
// }
// P->data=Q->data;
// if(P!=Pre2){
// Pre2->rchild=Q->lchild;
// }else{
// Pre2->lchild=Q->lchild;
// }
// delete(Q);
// cout<<"已删除 "<<a<<endl;
// }
if(P->lchild!=NULL&&P->rchild!=NULL){
tree* Q=P->rchild;
tree* Pre2;
while(Q->lchild){
Pre2=Q;
Q=Q->lchild;
}
P->data=Q->data;
if(P!=Pre2){
Pre2->lchild=Q->rchild;
}else{
Pre2->rchild=Q->rchild;
}
delete(Q);
cout<<"已删除 "<<a<<endl;
}
}
}
int main(){
tree* T=NULL;
int A[]={23,89,65,12,17,3,9,90,21,63,71};
Insert(T,A,10);
midorder(T);
delect(T,89);
midorder(T);
find(T,89);
midorder(T);
return 0;
}
结语:感谢观看,有什么问题望指正