二叉查找树分析与实现
二叉查找树有一下几个特点:
1,左孩子总是小于其父亲节点,右孩子总是大于其父亲节点
2,每颗子树都是一棵二叉查找树
3,通过中序遍历二叉树可以得到一个有序序列
作用:可以用来排序,可以在平均O(lgn)的时间的进行数据的快速查找,二叉查找数具有不稳定性,在建树的时候,有可能这个二叉树只有左子树或右子树,这样这颗二叉树就演化成了一个线性表了,那么他的查找时间复杂度最坏要为O(n),所有就又出现了一种对此树的一种弄个改进相似数据结构-平衡二叉树
基本操作: 建树,遍历,插入节点,删除,还有释放树
二叉查找树的存储结构:
typedef struct LNode
{
int data; //值
LNode *LChild; //左孩子
LNode *RChild;//右孩子
LNode *Parent;//父节点
} *STree;
注:要父节点的的目的主要是进行删除的时候需要用到父节点
建树和插入:其实就是一个不断的插入操作,每次插入都会新生成一个新的叶子节点
//插入操作
void insert(STree &root,int key){
LNode *p=(STree)malloc(sizeof(LNode));
p->data=key;
p->LChild=p->RChild=p->Parent=NULL;
if(NULL==root){ //如果树为空,则直接赋给根节点
root=p;
return;
}
if(NULL==root->LChild&&key<root->data){
root->LChild=p;
p->Parent=root;
return;
}
if(NULL==root->RChild&&key>=root->data){
root->RChild=p;
p->Parent=root;
return ;
}
if(root->data>key)
insert(root->LChild,key);
else
insert(root->RChild,key);
}
查找:查找步骤很简单,从根节点开始,如果此元素大于根节点,怎把指针指向其右孩子,反之则指向其左孩子,然后继续重复上述步骤。
//查找,成功返回1,失败0,用节点引用记录查找成功的节点
int Search(const STree T,STree &p,int n)
{
if(T&&T->data==n) //如果查找成功
{
p=T;
return 1;
}
if(T->LChild&&T->data>n) Search(T->LChild,p,n);
else if(T->RChild&&T->data<n) Search(T->RChild,p,n);
else
{
p=NULL;
return 0;
}
}
中序遍历:遍历出的结果是有序的
//中序遍历
void Mid(const STree T)
{
if(T==NULL)
return ;
Mid(T->LChild);
cout<<T->data<<" ";
Mid(T->RChild);
}
删除:删除操作稍微麻烦,分几种情况
1,删除节点a为叶子节点,则直接删除
2,删除节点a有一个左孩子或右孩子,则删除此节点,让其左孩子或右孩子的父节点变为被删除节点的父节点
3,删除节点a既有左孩子又有右孩子,这样就不是太简单了,首先我们要明白在中序遍历的时候,一个节点的前驱肯定没有右孩子,后继肯定没有左孩子,我们就找该节点的前驱或后继来替代当前节点,然后a的孩子节点的父节点为a的父节点。
//删除节点
void DeleteBST(STree &root,int n)
{
//欲删除节点,要找到该节点的位置,及是否有左右孩子
STree p; //用来记录查找到要删除的节点的位置和其父节点的位置
STree k=NULL; //缓冲节点,初始化为NULL是为了安全
int flag=Search(root,p,n); //查找删除节点是否存在,如果存在,则返回被删除节点p
if(flag==0) {
cout<<"要删除的节点不存在"<<endl;
return ;
}
else
{
//删除节点如果为叶子节点
if(NULL==p->LChild&&NULL==p->RChild)
{
if(!(p->Parent))//如果删除的是根节点
root=NULL;
else
{
//删除 节点是左孩子
if(p->Parent->LChild==p)
p->Parent->LChild=NULL;
else
p->Parent->RChild=NULL;
free(p);
}
}
//被删除节点只有一个左孩子
else if(!(p->RChild)){
//为根节点
if(NULL==p->Parent){
root=p->LChild;
root->Parent=NULL;
}
else{
if(p->Parent->LChild==p){
p->Parent->LChild=p->LChild;
p->LChild->Parent=p->Parent;
}
else{
p->Parent->RChild=p->LChild;
p->LChild->Parent=p->Parent;
}
}
free(p);
}
//只有一个右节点
else if(!p->LChild){
if(NULL==p->Parent){
root=p->RChild;
root->Parent=NULL;
}
else{
if(p->Parent->LChild==p){
p->Parent->LChild=p->RChild;
p->RChild->Parent=p->Parent;
}
else{
p->Parent->RChild=p->RChild;
p->RChild->Parent=p->Parent;
}
}
free(p); //释放节点
}
else
{
//找前驱
k=p->LChild;
while(k->RChild)
k=k->RChild;
//用前驱释覆盖被删除节点,并释放前驱的内存
p->data=k->data;
if(k->Parent->LChild==k){
k->Parent->LChild=k->LChild;
k->LChild->Parent=k->Parent;
}
else{
k->Parent->RChild=k->LChild;
k->LChild->Parent=k->Parent;
}
free(k);
}
}
}
释放树:递归释放,碰到虚节点,也就是NULL节点跳出递归
//释放树
void FreeTree(STree &T)
{
if(T==NULL)
return ;
FreeTree(T->LChild);
FreeTree(T->RChild);
free(T);
}
完整代码:
//二叉查找树创建、插入、删除
#include<iostream>
#include<stdio.h>
using namespace std;
typedef struct LNode
{
int data; //值
LNode *LChild; //左孩子
LNode *RChild;//右孩子
LNode *Parent;//父节点
} *STree;
//插入操作
void insert(STree &root,int key){
LNode *p=(STree)malloc(sizeof(LNode));
p->data=key;
p->LChild=p->RChild=p->Parent=NULL;
if(NULL==root){ //如果树为空,则直接赋给根节点
root=p;
return;
}
if(NULL==root->LChild&&key<root->data){
root->LChild=p;
p->Parent=root;
return;
}
if(NULL==root->RChild&&key>=root->data){
root->RChild=p;
p->Parent=root;
return ;
}
if(root->data>key)
insert(root->LChild,key);
else
insert(root->RChild,key);
}
//查找,成功返回1,失败0,用节点引用记录查找成功的节点
int Search(const STree T,STree &p,int n)
{
if(T&&T->data==n) //如果查找成功
{
p=T;
return 1;
}
if(T->LChild&&(T->data>n)) Search(T->LChild,p,n);
else if(T->RChild&&(T->data<n)) Search(T->RChild,p,n);
else
{
p=NULL;
return 0;
}
}
//中序遍历
void Mid(const STree T)
{
if(T==NULL)
return ;
Mid(T->LChild);
cout<<T->data<<" ";
Mid(T->RChild);
}
//删除节点
void DeleteBST(STree &root,int n)
{
//欲删除节点,要找到该节点的位置,及是否有左右孩子
STree p; //用来记录查找到要删除的节点的位置和其父节点的位置
STree k=NULL; //缓冲节点,初始化为NULL是为了安全
int flag=Search(root,p,n); //查找删除节点是否存在,如果存在,则返回被删除节点p
if(flag==0) {
cout<<"要删除的节点不存在"<<endl;
return ;
}
else
{
//删除节点如果为叶子节点
if(NULL==p->LChild&&NULL==p->RChild)
{
if(!(p->Parent))//如果删除的是根节点
root=NULL;
else
{
//删除 节点是左孩子
if(p->Parent->LChild==p)
p->Parent->LChild=NULL;
else
p->Parent->RChild=NULL;
free(p);
}
}
//被删除节点只有一个左孩子
else if(!(p->RChild)){
//为根节点
if(NULL==p->Parent){
root=p->LChild;
root->Parent=NULL;
}
else{
if(p->Parent->LChild==p){
p->Parent->LChild=p->LChild;
p->LChild->Parent=p->Parent;
}
else{
p->Parent->RChild=p->LChild;
p->LChild->Parent=p->Parent;
}
}
free(p);
}
//只有一个右节点
else if(!p->LChild){
if(NULL==p->Parent){
root=p->RChild;
root->Parent=NULL;
}
else{
if(p->Parent->LChild==p){
p->Parent->LChild=p->RChild;
p->RChild->Parent=p->Parent;
}
else{
p->Parent->RChild=p->RChild;
p->RChild->Parent=p->Parent;
}
}
free(p); //释放节点
}
else
{
//找前驱
k=p->LChild;
while(k->RChild)
k=k->RChild;
//用前驱释覆盖被删除节点,并释放前驱的内存
p->data=k->data;
if(k->Parent->LChild==k){
k->Parent->LChild=k->LChild;
k->LChild->Parent=k->Parent;
}
else{
k->Parent->RChild=k->LChild;
k->LChild->Parent=k->Parent;
}
free(k);
}
}
}
//释放树
void FreeTree(STree &T)
{
if(T==NULL)
return ;
FreeTree(T->LChild);
FreeTree(T->RChild);
free(T);
}
int main()
{
int temp;
int n,m;
STree root=NULL;
STree p=NULL;
//T->Parent=NULL;
cout<<"输入数的个数"<<endl;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>temp;
insert(root,temp);
}
Mid(root);
cout<<"输入删除的元素"<<endl;
cin>>m;
DeleteBST(root,m);
Mid(root);
FreeTree(root);
return 0;
}