二叉查找树
先说一下什么是二叉查找树,它是一种特殊的二叉树,其递归定义如下:
- 左子树任意节点值小于根节点,右子树任意节点值大于根节点
- 任意节点的左子树和右子树也是二叉查找树
- 任意节点的值不同
二叉查找树的功能主要有三点:
- 插入
- 删除
- 查询
但因为使用二叉查找树进行上述操作,所用时间和树的深度成正比,因此若有N个元素,操作的平均时间复杂度为O(logN),是一种极为高效的数据结构。
二叉查找树如何实现呢?我们先逐一分析。
查询
假设我们要查询数字X,我们先让X和根节点比,如果X小,则X在根节点的左子树中,反之在根节点的右子树中,然后继续按照上述规则与子树的根节点相比较,直到匹配成功。
插入
插入和查询类似,也是先和根节点比较,小就放左边,大就放右边,直到存在空节点并且满足构成的树是二叉查找树。
删除
删除较为复杂,因为一旦把中间某个节点删除,其子树便悬空了,那么我们如何处理这种情况呢?我们首先想到,能不能把子树的某一个节点提到该节点处,使之成为新的二叉查找树呢?这种想法是可行的,不过要遵从我们的目的:使之成为一棵新的二叉查找树。下面直接给出三个处理步骤:
- 如果删除节点没有左儿子,则直接把右儿子提上去。
- 如果删除节点左儿子没有右儿子,则把左儿子提上去。
- 如果不满足上两种情况,则把左儿子子孙中最大的提上去。
那么如何用代码实现呢?
我们使用结构体来表征每一个节点(每个节点拥有数据值以及指向左右儿子的指针)。
typedef struct BST{
int data;
struct BST *left,*right;
}node;
根据上述分析,插入元素代码如下:
node *insertData(node *ptr,int data)
{
if(ptr == NULL){ //空节点则构造新节点并返回
node *temp;
temp = (node *)malloc(sizeof(node));
temp->data = data;
temp->left = NULL; //将左右子树置空,预留位置
temp->right = NULL;
return temp;
}
else{
if(data < ptr->data) //如果小于,放在左子树
ptr->left = insertData(ptr->left,data);
else //如果大于,放在右子树
ptr->right = insertData(ptr->right,data);
return ptr;
}
}
查询代码如下:
int findData(node *ptr,int data) //返回1表示存在,返回0表示不存在
{
if(ptr == NULL) //如果空,表示没找到,返回0
return 0;
else if(ptr->data == data) //找到,返回1
return 1;
else if(data < ptr->data) //如果小于当前节点值,则从左子树继续找
return findData(ptr->left,data);
else //如果大于,则从右子树继续找
return findData(ptr->right,data);
}
删除代码如下:
node *deleteData(node *ptr,int data) //在ptr指向节点的子树及自己中删除data
{
if(ptr == NULL) //如果空节点,则返回NULL
return NULL;
else if(data < ptr->data) //如果小于,则从左子树继续进行
ptr->left = deleteData(ptr->left,data);
else if(data > ptr->data) //如果大于,则从右子树继续进行
ptr->right = deleteData(ptr->right,data);
else if(ptr->left == NULL){ //如果没有左子树,则把右子树提上去
node *temp = ptr->right;
free(ptr);
return temp;
}
else if(ptr->left->right == NULL){ //如果左儿子没有右儿子,把左儿子提上去
node *temp = ptr->left;
temp->right = ptr->right;
free(ptr);
return temp;
}
else{ //否则把左儿子最大子孙提上去
node *temp;
for(temp = ptr->left;temp->right->right != NULL;temp = temp->right)
;// 找到最大子孙的父亲
node *temp2 = temp->right; //temp2为最大子孙
temp->right = temp2->left; //最大子孙的父亲接过右儿子的左儿子
temp2->left = ptr->left; //最大子孙成功提上去,取代ptr
temp2->right = ptr->right;
free(ptr) ;
return temp2;
}
return ptr;
}
现在我们来测试一下,测试代码如下:
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
typedef struct BST{
int data;
struct BST *left,*right;
}node;
void print(node *root) //中序遍历
{
if(root){
cout<<root->data<<" ";
print(root->left);
print(root->right);
}
}
node *insertData(node *ptr,int data)
{
if(ptr == NULL){ //空节点则构造新节点并返回
node *temp;
temp = (node *)malloc(sizeof(node));
temp->data = data;
temp->left = NULL; //将左右子树置空,预留位置
temp->right = NULL;
return temp;
}
else{
if(data < ptr->data) //如果小于,放在左子树
ptr->left = insertData(ptr->left,data);
else //如果大于,放在右子树
ptr->right = insertData(ptr->right,data);
return ptr;
}
}
int findData(node *ptr,int data) //返回1表示存在,返回0表示不存在
{
if(ptr == NULL) //如果空,表示没找到,返回0
return 0;
else if(ptr->data == data) //找到,返回1
return 1;
else if(data < ptr->data) //如果小于当前节点值,则从左子树继续找
return findData(ptr->left,data);
else //如果大于,则从右子树继续找
return findData(ptr->right,data);
}
node *deleteData(node *ptr,int data) //在ptr指向节点的子树及自己中删除data
{
if(ptr == NULL) //如果空节点,则返回NULL
return NULL;
else if(data < ptr->data) //如果小于,则从左子树继续进行
ptr->left = deleteData(ptr->left,data);
else if(data > ptr->data) //如果大于,则从右子树继续进行
ptr->right = deleteData(ptr->right,data);
else if(ptr->left == NULL){ //如果没有左子树,则把右子树提上去
node *temp = ptr->right;
free(ptr);
return temp;
}
else if(ptr->left->right == NULL){ //如果左儿子没有右儿子,把左儿子提上去
node *temp = ptr->left;
temp->right = ptr->right;
free(ptr);
return temp;
}
else{ //否则把左儿子最大子孙提上去
node *temp;
for(temp = ptr->left;temp->right->right != NULL;temp = temp->right)
;// 找到最大子孙的父亲
node *temp2 = temp->right; //temp2为最大子孙
temp->right = temp2->left; //最大子孙的父亲接过右儿子的左儿子
temp2->left = ptr->left; //最大子孙成功提上去,取代ptr
temp2->right = ptr->right;
free(ptr) ;
return temp2;
}
return ptr;
}
int main(void)
{
node *root;
root = (node *)malloc(sizeof(node));
root = NULL;
root = insertData(root,8);
root = insertData(root,3);
root = insertData(root,10);
root = insertData(root,1);
root = insertData(root,6);
root = insertData(root,14);
root = insertData(root,4);
root = insertData(root,7);
root = insertData(root,13); //插入数据
print(root);
cout<<"\n";
cout<<findData(root,20)<<"\n";
cout<<findData(root,10)<<"\n"; //查找数据
deleteData(root,10); //删除数据
print(root);
cout<<"\n";
return 0;
}
ouput:
8 3 1 6 4 7 10 14 13
0
1
8 3 1 6 4 7 14 13