/*
编程实现二叉排序树的基本操作,注意如果有多个值相同的元素,
则除了第一个之外其余的不能插入到树中,因为这样会使树的高度
增大过快。
样例输入:
50 70 20 60 40 30 10 90 80
*/
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int TYPE;//数据类型别名
/*声明二叉树结点结构体*/
typedef struct Node
{
TYPE data;//存储数据
struct Node* left;//记录左子树地址
struct Node* right;//记录右子树地址
}Tree,Node;
void init(Tree** root);//初始化,构造空二叉树
bool create(Tree** root);//构造二叉树
//插入新的节点,用于上面的构造二叉树函数中
bool insert(Tree** root,Node* p);
bool isEmpty(Tree* root);//判断二叉树是否为空
void traverse(Tree* root);//中序遍历二叉排序树
/*
函数说明:
这个函数的返回值本来用一级指针就可以了,但为了让后边的删除操作也使用该返回值
,这里用了二级指针,因为在删除操作中要修改指针本身中的内容,用一级指针返回的
是一个拷贝的地址值,出了函数就没法再使用,而用二级指针就可以修改它指向的一级
指针里的内容了
*/
Node** search(Tree** root,TYPE elem);//查找指定元素在二叉树的地址
int length(Tree* root);//计算树中结点的个数
TYPE get_root(Tree* root);//获取根节点的元素值
//修改指定元素值为新值,需要重新排列,先删除,再插入
bool update(Tree** root,TYPE elem_Old,TYPE elem_New);
bool Delete(Tree** root,TYPE elem);//删除指定元素,需要重新排列,需要借助查找操作
void clear(Tree** root);//清空二叉树
int main()
{
Tree* root;
init(&root);
printf("树中结点个数:%d\n",length(root));
//构造二叉排序树
int count=9;
while(count--)
{
create(&root);
}
//遍历二叉排序树
traverse(root);
printf("\n");
printf("树中结点个数:%d\n",length(root));
printf("根节点的值是:%d\n",get_root(root));
//查找指定元素的地址
printf("输入要查找的元素值:");
TYPE elem=0;
scanf("%d",&elem);
Node** p=search(&root,elem);
if(p)
printf("查找的元素是:%d\n",(*p)->data);
else
printf("查找失败!\n");
//删除二叉树中指定节点
printf("请输入要删除的结点值:");
scanf("%d",&elem);
bool flag=Delete(&root,elem);
if(flag)
{
printf("删除成功!\n");
traverse(root);
printf("\n");
printf("删除后树中结点个数:%d\n",length(root));
printf("删除后根节点的值是:%d\n",get_root(root));
}
else
printf("未找到指定元素,删除失败!\n");
//更新指定节点为新值
printf("输入要更新的旧值和新值:");
TYPE elem_Old=0,elem_New=0;
scanf("%d%d",&elem_Old,&elem_New);
update(&root,elem_Old,elem_New);
traverse(root);
printf("\n");
//清空二叉树
clear(&root);
return 0;
}
//初始化,构造空二叉排序树
void init(Tree** root)
{
*root=NULL;
}
/*
函数功能:构造二叉排序树
函数描述:
在插入节点时分两种情况,一种是树目前为空,所以直接插入新的结点。
第二种是树不为空,待插入的结点需要和树中原来的结点值比较,
寻找插入的位置,此时调用了insert函数。
*/
bool create(Tree** root)
{
TYPE elem=0;
scanf("%d",&elem);
Node* p=(Node*)malloc(sizeof(struct Node));
if(p)
{
p->data=elem;
p->left=NULL;
p->right=NULL;
bool flag;
if(isEmpty(*root))
*root=p;
else
flag=insert(root,p);
return flag;
}
else
return false;
}
//判断二叉树是否为空
bool isEmpty(Tree* root)
{
return NULL==root;
}
//插入新的节点
bool insert(Node** root,Node* p)
{
if(isEmpty(*root))//如果二叉树为空,则直接插入结点
*root=p;
else//处理不为空的情况
{
if((p->data)<((*root)->data))//小于根向左插
{
insert(&((*root)->left),p);
}
else if((p->data)>((*root)->data))//大于根向右插
{
insert(&((*root)->right),p);
}
else//等于根节点插入失败
return false;
}
return true;
}
//中序遍历二叉排序树
void traverse(Node* root)
{
if(NULL!=root)
{
traverse(root->left);
printf("%d ",root->data);
traverse(root->right);
}
}
/*
函数说明:获取根节点的元素值
函数描述:当根节点存在时,返回根节点的值,当根结点不存在时,
返回-1代表失败。注意,由于用-1代表了未获取到,所以
在插入结点时不要插入值为-1的结点。
*/
TYPE get_root(Tree* root)
{
if(root)
return root->data;
else
return -1;
}
/*
函数功能:计算树中结点的个数。
注意:
这个函数由于递归计算节点个数,所以使用了静态局部变量,
但是也留下了问题,当下一次调用该函数时count的值会从上次
保留的值开始递增,所以会出错。改成全局变量即可。
*/
int length(Tree* root)
{
static int count=0;
if(root)
{
count++;
length(root->left);
length(root->right);
return count;
}
else
return 0;
}
//查找指定元素在二叉树中的地址
Node** search(Tree** root,TYPE elem)
{
if(!isEmpty(*root))//判断树是否为空
{
if(elem>(*root)->data)
return search(&((*root)->right),elem);//大于的话查找右子树
else if(elem<(*root)->data)
return search(&((*root)->left),elem);//小于的话查找左子树
else
return &(*root);//找到的话返回地址
}
else
return NULL;//树为空则返回NULL
}
/*
函数说明:修改指定元素值为新值
函数描述:
需要重新排列,先删除,再插入。注意,这个函数在先删除原来结点
再插入新的结点后,会改变树的原结构。
*/
bool update(Tree** root,TYPE elem_Old,TYPE elem_New)
{
Delete(root,elem_Old);
Node* p=(Node*)malloc(sizeof(struct Node));
if(p)
{
p->data=elem_New;
p->left=NULL;
p->right=NULL;
return insert(root,p);
}
else
return false;
}
/*
函数功能:删除指定结点
函数描述:
该函数首先在树中搜索要查找的结点,如果找到后,
返回该结点的前驱结点中指向该节点的指针的地址(left或者right指针的地址),
即返回一个二级指针,然后通过这个二级指针来修改它指向的一级指针的值来
达到删除节点的效果。
*/
bool Delete(Tree** root,TYPE elem)
{
//首先找到这个结点,*q位于这个结点的前驱结点中,代表left或者right指针
//它指向当前结点
Node** p=search(root,elem);
if(p)//查找到的话删除
{
Node* q=NULL;
//前两种情况包含了删除的结点是叶子节点的情况,即左右子树均为空
if(NULL==(*p)->left)//1如果当前节点的左子树为空,则直接接上它的右子树
{
q=*p;
*p=(*p)->right;
free(q);
return true;
}
else if(NULL==(*p)->right)//2如果当前节点的右子树为空,则直接接上它的左子树
{
q=*p;
*p=(*p)->left;
free(q);
return true;
}
else//3如果当前节点的左右子树都不为空
{
//为了删除节点后树还保持有序性,需要找到当前节点的中序遍历时的直接前驱
//它的前驱肯定在它的左子树中最后被遍历到的那一个结点。
Node* s=(*p)->left;//进入左子树
while(s->right)//找到最右边的结点,即最后遍历的结点,用s记录
{
q=s;//q记录当前要找结点的上一结点
s=s->right;//s指向当前要找的结点
}
if(NULL==q)//因为如果上面的循环没有执行,q的值就没有改变
{
(*p)->data=s->data;
//如果s没有右子树,那么上面的循环就不会执行,所以重接s的左子树
(*p)->left=s->left;
free(s);
}
else
{
(*p)->data=q->right->data;
q->right=s->left;
free(s);
}
return true;
}
}
else//查找不到删除失败
return false;
}
//清空二叉树
void clear(Tree** root)
{
if(*root)
{
clear(&((*root)->left));//清空左子树
clear(&((*root)->right));//清空右子树
free(*root);//释放根节点
*root=NULL;
}
}
二叉排序树的实现
最新推荐文章于 2022-07-29 12:38:41 发布