二叉排序树(基本操作)

这篇博客介绍了二叉排序树(二叉搜索树)的基本操作,包括非递归和递归查找、插入以及删除功能的实现。文章强调了在进行插入和删除操作时需要找到父节点的重要性,以保持树的正确结构。此外,还提到了二叉排序树的平均查找长度(ASL)和平衡对于提高查找效率的影响。最后,提供了代码示例和中序遍历来验证操作的正确性。
摘要由CSDN通过智能技术生成

二叉排序树的基本操作: 1、查找   2、增加   3、删除。

代码展示及结果截图:

#include<stdio.h>
#include<stdlib.h>
/*
	二叉排序树  <==> 二叉搜索树 (BST) <==>  二叉查找树 
	
	1. 二叉排序树的查找功能 (递归、非递归实现)    --> 查找效率很大程度上取决于二叉排序树的高度。 
	2. 二叉排序树的插入功能 (默认不同元素的值才插入) --> 先查找再插入 
	3. 二叉排序树的删除功能 (两种实现方式, 1.后继结点顶上来,删除后继结点 2.前驱结点顶上来,删除前驱结点)  --> 先查找再删除 
	4. 根据一个序列,构造二叉排序树的功能。  --> 其实就是遍历序列 插入二叉排序树。 
	
	二叉排序树的一个重要指标 : 平均查找长度(ASL)
		平均查找长度又分: 查找成功的平均查找长度    sum(查找每个元素的比较次数)/元素个数  
						  查找失败的平均查找长度    sum(查找每个失败元素时比较次数)/失败出现的可能数 
  		结论: 二叉排序树最好尽可能平衡,因为这样的查找效率可以达到O(log n).  
		 
	
	
*/ 
#define ElemType int
#define null NULL
typedef struct BSTNode{
	ElemType key;
	struct BSTNode *lchild,*rchild;
}BSTNode;

//二叉搜索树查找功能的非递归实现   时间复杂度O(log n) - O(n) 
BSTNode* findKey(BSTNode *t,ElemType value){ //最坏空间复杂度O(1) 
	//t == null 代表查找元素不存在   
	while(t != null && value != t->key){
		if(t->key > value){
			t = t->lchild;
		}else{
			t = t->rchild;
		}	
	}
	return t;
}
//二叉搜索树的查找功能(递归实现)
BSTNode* findKey2(BSTNode *t, ElemType value){ //最坏空间复杂度O(h)  h是二叉搜索树的最大高度。 
	if(t == null){
		return null;
	}
	if(t->key == value){
		return t;	
	}
	if(t->key > value){
		return findKey2(t->lchild,value);	
	}else{
		return findKey2(t->rchild,value);
	}
} 

//二叉搜索树的插入功能 (默认不同元素的值才插入) 
bool BST_insert(BSTNode **t, ElemType value){
	//插入第一个结点 
	if(*t == null){
		BSTNode *s = (BSTNode *)malloc(sizeof(BSTNode));
		s->key = value;
		s->lchild = null;
		s->rchild = null;
	 	*t=s;
		return true; 		 
	}
	if(((*t)->key == value)){
		return false;
	}else if((*t)->key > value && (*t)->lchild == null){
		BSTNode *s = (BSTNode *)malloc(sizeof(BSTNode));
		s->key = value;
		s->lchild = null;
		s->rchild = null;
		(*t)->lchild=s;
		return true; 
	}else if((*t)->key < value && (*t)->rchild == null){
		BSTNode *s = (BSTNode *)malloc(sizeof(BSTNode));
		s->key = value;
		s->lchild = null;
		s->rchild = null;
		(*t)->rchild=s;
		return true; 
	}else{
		//递归寻找
		if((*t)->key > value){
	 		BST_insert(&((*t)->lchild),value);	
		}else{
	 		BST_insert(&((*t)->rchild),value);	
		}
	}
} 
//根据序列构造二叉搜索树
BSTNode* Creat_BST(ElemType str[],int n){
	BSTNode *t = null;  //初始时t为空树 
	for(int i = 0;i < n;i++){  //依次将每个关键字插入到二叉排序树中 
		
		BST_insert(&t,str[i]);
		
	}
	return t;	
}

//二叉搜索树的删除功能
/*
	1.删除的结点是叶子结点
	2.删除的结点只有左子树或者只有右子树 
	3.删除的结点既有左子树又有右子树 
*/
//value是要删除的元素值
//不应该找到要删除的元素,而是应该找到要删除元素的前一个元素。 
BSTNode* BST_delete(BSTNode *b,ElemType	value){
	BSTNode *t = b; 
	//f指针指向的是要删除结点的双亲 
	BSTNode *f = null;
	//判断t是f的哪个孩子(左孩子 or 右孩子) 
	int child = 0;
	while(t != null && t->key != value ){
		f=t; 
		if(t->key > value){
			child = -1;
			t= t->lchild;
		}else{
			child = 1;
			t=t->rchild;
		}
	}
	
	//没找到  删除失败 
	if(t == null){
		return b; 
	}
	
	 //删除的是根节点,并且左右子树都没有 
	 if(f == null && t -> rchild == null && t->lchild == null ){
	 	b = null;
	 	free(t);
 		return b;
 	}
 	//删除的是根节点,并且没有左子树,只有右子树 
	if(f == null && t->lchild == null && t->rchild	!= null){
		b=t->rchild;
		free(t);
		return b;
	}
	
	//删除的是根节点,并且没有右子树,只有左子树 
	if(f == null && t->rchild == null && t->lchild	!= null){
		b=t->lchild;
		free(t);
		return b;
	}
	
	
	//删除的是叶子结点 
	if(t->lchild == null && t->rchild == null){
		free(t);
		//删除的结点是他爹的左孩子
		if(child == -1){
			f->lchild = null; 
		}
		if(child == 1){
			f->rchild = null;	
		}
		return b; 		
	}
	//删除的结点只有右子树,没有左子树 
	 if(t->lchild == null && t->rchild != null){
	 	//删除的结点是他爹的左孩子
 		if(child == -1){
		 	f->lchild = t->rchild;
		 	free(t);	
	 	}
	 	if(child == 1){
	 		f->rchild = t->rchild;
			free(t); 
	 	}
	 	return b;
 	}
	  
	//删除的结点只有左子树,没有右子树 
	if(t->lchild != null && t->rchild == null){
		//删除的结点是他爹的左孩子 
 		if(child == -1){
		 	f->lchild = t->lchild;
		 	free(t);	
	 	}
	 	if(child == 1){
	 		f->rchild = t->lchild;
			free(t); 
	 	}
	 	return b;
	} 
	//删除的结点既有左子树又有右子树 
	if(t->lchild != null && t->rchild != null ){
		//两种方式解决  1.将右子树中最小的元素放到此位置,并删除右子树中的最小元素。 
		//              2.将左子树中最大的元素放到此位置,并删除左子树中的最大元素。
		//方式1代码 
		//右子树中最小的元素,就是右子树中最左下角的元素.
	//	BSTNode *q = t->rchild;
	//	while(q->lchild != null ){
	//		q = q->lchild;
	//		
	//	}
		//q即为要查找的右子树的最小元素
	//1
		// ElemType temp = q->key;
		 //右子树中删除值最小的元素 , 先删除再赋值 
		 //BST_delete(b,q->key);
		 //t->key = temp;
	//2	 巧妙的利用了返回值,无缝将右子树部分删除掉q并衔接。 
     //    t->key = q->key; 
	   //  t->rchild = BST_delete(t->rchild,q->key);	 
		 
		//方式2代码 
		//左子树中最大的元素,就是左子树中最右下角的元素。 
		
		BSTNode *z = t->lchild;
		while(z->rchild != null){
			z = z->rchild;
		} 
		//z即为要查找的左子树的最大元素
		t->key = z->key;
		t->lchild = BST_delete(t->lchild,z->key); 
		return b;
	} 	
}




	
//二叉树的中序遍历
void in_order(BSTNode *t){
	if(t != null){
		in_order(t->lchild);
		printf("%d ",t->key);
		in_order(t->rchild);
	}
}

int main(int argc, char *argv[])
{
	ElemType str[] = {19,13,11,8,50,66,70,60,63,61,65,26,30,21};
	BSTNode *m = Creat_BST(str,14);
	in_order(m);
	printf("\n");
	BSTNode *end = BST_delete(m,70); 
	printf("\n");
	in_order(end);
	return 0;
}

 注意:

二叉搜索树在插入删除一个结点的时候,不能单单只找到当前要插入结点的位置、要删除的结点。

还要找到要插入结点位置的前一个结点,要删除结点的父结点。因为无论是插入还是删除,父结点指针的指向都需要改变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值