二叉搜索树Binary Search Tree (BST)

二叉搜索树Binary Search Tree (BST)

今天数据结构学习了BST。BST是基于二叉树创建的一种数据结构,也叫二叉查找树。其他很多的数据结构,如AVL树,Splay树等,都是基于BST的升级版本。对BST拥有全面的理解,是学好更高级别的查找树的基础。

  • 功能:用来查找特定的元素是否存在。 操作:对于特定的元素,可以有查询,插入,删除等操作。对于整棵BST,可以查找最大、最小值。
  • 实现基础:最简单的BST建立在集合上,也就是说,没有重复元素。这也是集合的实现方式之一。而建立在序列上的BST则可以含有重复元素。元素之间必须可以比较大小,或者由使用者自行定义比较大小的方式。BST的节点含有键值,存储的就是对象。比如1-100的正整数集合,数之间可以比较大小,元素有限,键值存储的就是1-100的正整数。
  • 实现原理:利用递归定义的方法。BST中的某个节点node,其键值为q。那么,键值小于q的节点被存储在node的左子树中,大于q的节点被存储在node的右子树中。

查找

BST find( BST T, element_type value ){ //返回查找到的节点。如果没有就返回NULL 
	
	if( T == NULL ){
		printf("We can't find it.\n") ;
		return NULL ;
	}
	if( T->key == value ){
		return T ;
	}
	else if( T->key < value ){
		return find( T->right, value ) ;
	}
	else return find( T->left, value ) ;
}

了解BST的实现原理之后,想要查找一个元素m是否存在,就比较容易了:从根节点开始,比较键值q和m。如果q==m,那么正好就找到了。如果m更小,就去左子树里面找;反之就去右子树里面找。

插入

BST Insert( BST T, element_type value ){
	//要返回变化后的树,这样才能保证修改的信息能传递上去
	if( T == NULL ){
		T = (BST) malloc ( sizeof(BST) ) ;
		T->key = value ;
		T->left = T->right = NULL ;
	}
	if( T->key < value ) {
		T->right = Insert( T->right, value ) ;
	}
	if( T->key > value ) {
		T->left = Insert( T->left, value ) ;
	}
	return T ; //返回变化后的树 
}

一样的利用大小比较的查找原理。最终会找到元素应该在的位置。这时候要申请一块内存,存储这样一个新的元素。记住:无论是创建了新的元素,还是修改了左右子树,最后都一定要返回变化过的树!

删除

删除的操作比较复杂。不过我们最先要这个元素做的依然是找到要删除的元素。在找到元素时候,问题就来了:怎么删除呢?

  1. 如果要删除的节点是叶子结点,那么,恭喜你!直接删掉就可以了,不需要任何其他的操作。
  2. 如果这个节点有一颗子树(无论左右),那么把这棵子树链接到这个节点的父节点上即可。(想想为什么,可以分情况讨论一下)
  3. 节点有两颗子树,这种情况是比较麻烦的。我们可以找比要删除的元素——设为p——大的最小的元素——设为q。也就是在有序的升序序列中,找p的下一个元素q来代替p的位置。那么在BST中,q在哪里呢?回忆BST的构建原理,我们可以知道,q在p的右子树中,并且是右子树中的最小元素。这样,类我们把q拿过来,放到p的位置上。对于原来p的左子树来说,q比p还要大,自然没有问题;对于p的父节点来说,p和它的子树都是同一种类型,也没有问题;只剩下右子树了。q被拿走了,右子树怎么办?这下你会发现,只要先从右子树里删掉q不就行了吗!这样,第3类删除节点就变成了一个递归型的操作,最后总会归结到前两类中。

不过,这个递归并不会持续很长,因为q一定是没有左子树的(所以是个假递归 )。思考:如果q有左子树,那么就是说有着比q还要小的元素,并且这些元素也在p的右子树中。这样q又怎么能成为p的右子树中最小的元素呢?所以,找到节点之后,最多递归一次,我们就可以完成删除了。

要用到的找最小元素的代码
BST find_min( BST T ){ //循环版本好像也挺好理解的...还比较安全 
	if( is_empty( T ) ) {
		printf("Wrong! The tree is empty.\n") ;
		return NULL ;
	}
	while( T->left != NULL ) T = T->left ;
	return T ;
}

一直往左子树上找就可以了。

真正的删除节点的代码
BST Delete( BST T, element_type value ){ 
	//1.先找元素
	//2.找到后删除,分情况讨论 
	if( T == NULL ){
		printf("Wrong! The value is not included.\n") ;
		return NULL ;
	}
	if( value < T->key ) {
		T->left = Delete( T->left, value ) ;
	}
	else if( value > T->key ) {
		T->right = Delete( T->right, value ) ;
	}
	else {
		BST child = NULL, tmp_cell ;
		if( T->left && T->right ){ //这个点被保留了, 但是更改了键值和子树信息,返回T 
			tmp_cell = find_min( T->right ) ;
			T->key = tmp_cell->key ;
			T->right = Delete( T->right, tmp_cell->key ) ;
		}
		else { //T这个点被扔掉了,返回了T的子树 
			if( T->left ) child = T->left ;
			if( T->right ) child = T->right ;
			free(T) ;
			return child ;
		}
	}
	return T ; //!!!!
}

小问题:代码中的free(T)调试的时候会造成错误,还不知道为什么…

附上完整的程序:

//试图写一个没有重复元素的BST(Binary Search Tree)——二叉搜索树
//左子树小,右子树大
//可以任意规定元素类型,但是元素之间必须可以比较(或者自行定义) 
#include<stdio.h>
#include<stdlib.h>
typedef int element_type ;
typedef struct tree_node *tree_ptr ; 
struct tree_node{
	element_type key ;
	tree_ptr left ;
	tree_ptr right ;
};
typedef tree_ptr BST ;

int is_empty( BST T ){
	return ( T == NULL ) ;
}

BST Build(){ //创建BST,其实就是空树
	return NULL ;
}

BST find( BST T, element_type value ){ //返回查找到的节点。如果没有就返回NULL 
	
	if( T == NULL ){
		printf("We can't find it.\n") ;
		return NULL ;
	}
	if( T->key == value ){
		return T ;
	}
	else if( T->key < value ){
		return find( T->right, value ) ;
	}
	else return find( T->left, value ) ;
}

BST find_max( BST T ){ //尾递归版本,容易爆栈,不过简单好理解 
	if( is_empty( T ) ) {
		printf("Wrong! The tree is empty.\n") ;
		return NULL ;
	}
	if( T->right == NULL ) {
		return T ; 
	}
	return find_max( T->right ) ;
}

BST find_min( BST T ){ //循环版本好像也挺好理解的...还比较安全 
	if( is_empty( T ) ) {
		printf("Wrong! The tree is empty.\n") ;
		return NULL ;
	}
	while( T->left != NULL ) T = T->left ;
	return T ;
}

BST Insert( BST T, element_type value ){
	if( T == NULL ){
		T = (BST) malloc ( sizeof(BST) ) ;
		T->key = value ;
		T->left = T->right = NULL ;
	}
	if( T->key < value ) {
		T->right = Insert( T->right, value ) ;
	}
	if( T->key > value ) {
		T->left = Insert( T->left, value ) ;
	}
	return T ; //返回变化后的树 
}

BST Delete( BST T, element_type value ){ 
	//1.先找元素
	//2.找到后删除,分情况讨论 
	if( T == NULL ){
		printf("Wrong! The value is not included.\n") ;
		return NULL ;
	}
	if( value < T->key ) {
		T->left = Delete( T->left, value ) ;
	}
	else if( value > T->key ) {
		T->right = Delete( T->right, value ) ;
	}
	else {
		BST child = NULL, tmp_cell ;
		if( T->left && T->right ){ //这个点被保留了, 但是更改了键值和子树信息,返回T 
			tmp_cell = find_min( T->right ) ;
			T->key = tmp_cell->key ;
			T->right = Delete( T->right, tmp_cell->key ) ;
		}
		else { //T这个点被扔掉了,返回了T的子树 
			if( T->left ) child = T->left ;
			if( T->right ) child = T->right ;
			free(T) ;
			return child ;
		}
	}
	return T ; //!!!!
}

void infix_traversal( BST T ){ //中序遍历BST就能从小到大输出序列 
	if( T == NULL ) return ;
	infix_traversal( T->left ) ;
	printf("%d ", T->key ) ;
	infix_traversal( T->right ) ;
}

int main(){
	int a[]={1,2,3,5,6,8,9} ;
	int n=7, i=0 ;
	BST T = Build() ;
	for(i=0 ; i<n ; i++ ){
		T = Insert( T, a[i] ) ;
	}
	
	T = Delete( T, 5 ) ;
	infix_traversal(T) ;
	return 0 ;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值