二叉搜索树(BST) ----- C语言


二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树

二叉排序树的查找过程和次优二叉树类似,通常采取二叉链表作为二叉排序树存储结构中序遍历二叉排序树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉排序树变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。每次插入的新的结点都是二叉排序树上新的叶子结点,在进行插入操作时,不必移动其它结点,只需改动某个结点的指针,由空变为非空即可。搜索、插入、删除的复杂度等于树高,查找平均情况为O(log(n)),最坏情况为O(n)。故还需优化,后续将说明平衡二叉树(AVL树)的实现。





源代码-----------------------------------------


/*	
*	二叉搜索树(BST)常用于动态查找,由于其在查找和插入删除方面
*	有较高的效率,因而得到广泛应用。其主要操作就是插入、删除和查找。
*	表中存在则不插入关键字,否则插入;
*	查询操作可通过递归分左子树跟右子树查询;
*	删除操作较复杂。
*
*	2015.12.05
*	By Snow Yong
*
*/

#include <stdio.h>
#include <stdlib.h>

typedef struct Binode
{
	int data;
	struct Binode *lchild, *rchild;
}Binode, * Bitree;/*二叉树数据结构声明*/

int SearchBST(Bitree T, int key, Bitree f, Bitree *p);
int InsertBST(Bitree *T, int key);
int DeleteBST(Bitree *T, int key);
int Delete(Bitree *p);


/*
*二叉搜索树关键字查询函数, T为要搜索的二叉树的当前节点,
*key为目标关键字,f用来存放当前节点T的父节点,初始
*传递值为NULL,如果查询不到此关键字,则将f赋给*p,以便
*后续的插入和删除节点的操作,若找到此关键字,则将当前
*节点T赋给*p。(注意:二叉树T通过不断递归操作来实现查找)
*/
int SearchBST(Bitree T, int key, Bitree f, Bitree *p)
{
	if (!T)
	{//如果当前二叉搜索树T不存在,则将f赋给*p
		(*p) = f;
		return 0;//返回0,查找失败
	}
	else if (key == T->data)
	{//当前节点T的值等于查找的关键字,将T赋给*p
		(*p) = T;
		return 1;//返回1,查找成功
	}
	else if (key < T->data)
	{//当前节点值大于查找的关键字,递归从当前节点左子树查找
		return SearchBST(T->lchild, key, T, p);
	}
	else
	{//当前节点值小于查找的关键字,递归从当前节点右子树查找
		return SearchBST(T->rchild, key, T, p);
	}
}


//二叉搜索树插入节点操作
int InsertBST(Bitree *T, int key)
{
	Bitree p, s;
	
	if (!SearchBST(*T, key, NULL, &p))
	{//如果欲插入的关键字不存在于当前树中,则插入,p作为最后搜索到的节点
		s = (Bitree)malloc(sizeof(Binode));			//创建节点,分配内存
		s->data = key;
		s->lchild = s->rchild = NULL;
		
		if (!p)
		{//此处if条件解释为:若p为NULL,那么表明*T是一颗空树,直接赋值插入
			(*T) = s;
		}
		else if (key < p->data)
		{//如果插入关键字小于p(最后搜索到的节点)值,则s为p的左孩子
			p->lchild = s;
		}
		else
		{//如果插入关键字大于p(最后搜索到的节点)值,则s为p的右孩子
			p->rchild = s;
		}
		return 1;//返回1,插入成功
	}
	else
	{//否则返回0,插入失败
		return 0;
	}
}/*二叉搜索树插入节点操作*/



//二叉搜索树删除节点操作
int DeleteBST(Bitree *T, int key)
{
	if (!(*T))
	{//若*T是空树,删除失败
		return 0;
	}
	else if (key == (*T)->data)
	{//若当前节点就是要删除的关键字节点,调用Delete函数
		return Delete(T);
	}
	else if (key < (*T)->data)
	{//若关键字小于当前节点值,递归左子树搜索
		return DeleteBST(&((*T)->lchild), key);
	}
	else
	{//若关键字大于当前节点值,递归右子树搜索
		return DeleteBST(&((*T)->rchild), key);
	}
}

//此函数用于删除节点并调节删除后的树结构以保证满足二叉搜索树性质
int Delete(Bitree *p)
{
	Bitree q, s;
	
	if ((*p)->rchild == NULL)			//如果欲删除节点的右子树为空,
	{									//则直接用左孩子替换并释放空间
		q = (*p);
		(*p) = (*p)->lchild;
		
		free(q);
	}
	else if ((*p)->lchild == NULL)		//如果欲删除节点的左子树为空,
	{									//则直接用右孩子替换并释放空间
		q = (*p);
		(*p) = (*p)->rchild;
		
		free(q);
	}
	else
	{									//如果欲删除节点既有左子树也有右子树,则取中序遍历最接近欲删除节点的节点来替换
		q = (*p);						//如:要删除的节点为*p,则由中序遍历知道,取*p左孩子,再循环取*p左孩子的右孩子
		s = (*p)->lchild;				//q = (*p)是为了处理*p左孩子s不含有右孩子的情况
										
		while (s->rchild)				//循环将s替换为s的右孩子,q则存储原来的s(即新s的父节点)
		{
			q = s;
			s = s->rchild;
		}
		
		(*p)->data = s->data;			//将s的值赋给欲删除节点*p的值,最后删除s节点即可
		
		if (*p != q)					//如果*p != q,则此时q是s的父节点,善后处理需将s的左孩子赋给父节点q的右孩子
		{								//注意:此时s已不含右孩子,删除s节点后,q要补全右孩子,故q->rchild = s->lchild
			q->rchild = s->lchild;
		}
		else
		{								//如果*p == q,则说明s不含右孩子,直接将s的左孩子赋给q的左孩子
			q->lchild = s->lchild;
		}
		
		free(s);						//释放空间
	}
	
	return 1;
}


//中序遍历二叉树,输出的二叉树是递增有序的,
//若不是,则说明二叉搜索树存在bug
int InOrderView(Bitree T)
{
	if (T == NULL)
	{
		return 0;
	}
	
	InOrderView(T->lchild);
	printf("%d--", T->data);
	InOrderView(T->rchild);
}

int main()
{
	int i;
	int a[10] = {33, 4, 12, 58, 99,
				40, 67, 55, 1, 120};
	Bitree p, T = NULL;
	
	for (i = 0; i < 10; i++)
	{
		InsertBST(&T, a[i]);
	}
	
	InOrderView(T);
	puts("\n");
	
	SearchBST(T, 120, NULL, &p);
	printf("Search Result: %d\n", p->data);
	
	DeleteBST(&T, 55);
	
	InOrderView(T);
	puts("\n");
	return 0;
}




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值