二叉排序树的实现

/*
编程实现二叉排序树的基本操作,注意如果有多个值相同的元素,
则除了第一个之外其余的不能插入到树中,因为这样会使树的高度
增大过快。

样例输入:
		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;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秦时小

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值