BST 二叉搜索树 非指针版

BST

  BST即为binarysearch tree 二叉搜索树为平衡树的基础,所以很要学习的必要。

  BST的结点满足一个基本条件就是:结点k的左子树中所有值小于结点k的值,结点k的右子树的所有值大于结点k的值,显然这是一个递归定义的结构。

  这个结构就使得中序遍历出来的序列即是一个升序的序列。一个优秀的二叉搜索树能够在log(n)的时间内进行查找、删除一个元素等。

  在这里我没有使用指针。当然指针版的更为方便好用。

  首先是建树的过程,因为在这里没有使用指针,所以用0来表示空结点,1表示root结点。建树为递归过程。

  插入和查找操作两者相似,且比较简单。唯一复杂点的操作就是删除操作。下面分几种情况来讨论:

1.  需要删除的结点为叶子结点,即此结点没有左儿子和右儿子。直接将此结点的父亲结点的信息更新为不指向此结点即可。

2.  需要删除的结点只有一个儿子,左儿子或者右儿子。对于只有左儿子的情况,只需要将左儿子的父亲更新为此结点的父亲,同时更新此结点父亲的指向。右儿子类似。

3.  需要删除的结点既有左儿子又有右儿子。这种情况稍微麻烦一点,可考虑将右子树中最小值赋值给待删结点,这样保证了树的结构,然后再将右子树中最小值结点按叶子结点,即情况1的方法删除。这里还涉及了查询最小值操作。


/**********************************************
  随机化二叉搜索树 插入 查找 删除 操作 非指针版
***********************************************/
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<time.h>
#define rep(i,n) for(int i=0;i<n;i++)
#define rrep(i,n) for(int i=1;i<=n;i++)
using namespace std;
struct TreeNode
{
	int key,freq;//frep纪录此值出现的次数
	int lson,rson,fa;
};
const int maxn=10100;
TreeNode tree[maxn];
int A[maxn];
int root=1,n,m,x,tot;//root为1 空结点用0表示
//插入操作
void insert(int k,int x)
{
	if(tree[k].freq==0){
		tree[k].key=x;
		tree[k].freq=1;
		return;
	}
	if(x==tree[k].key){
		tree[k].freq++;
		return;
	}
	//插入为左儿子
	if((x<tree[k].key)&&(tree[tree[k].lson].freq==0)){
		tot++;
		tree[k].lson=tot;
		tree[tot].freq=1;
		tree[tot].key=x;
		tree[tot].freq=1;
		tree[tot].fa=k;
		return;
	}

	//插入为右儿子
	if((x>tree[k].key)&&(tree[tree[k].rson].freq==0)){
		tot++;
		tree[k].rson=tot;
		tree[tot].freq=1;
		tree[tot].key=x;
		tree[tot].freq=1;
		tree[tot].fa=k;
		return;
	}

	if(x<tree[k].key)insert(tree[k].lson,x);
	else insert(tree[k].rson,x);
}     

//查找最小值
int searchmin(int k)
{
	if(tree[k].lson==0)return tree[k].key;
	return searchmin(tree[k].lson);
}

//查找最大值
int searchmax(int k)
{
	if(tree[k].rson==0)return tree[k].key;
	return searchmax(tree[k].rson);
}

bool del(int k,int x)
{
	if(tree[k].freq==0)return 0;
	if(x<tree[k].key){return del(tree[k].lson,x);}
    if(x>tree[k].key){return del(tree[k].rson,x);}
//	if(tree[k].freq>1){tree[k].freq--;return 1;}//一个值出现了多次  

	//叶子结点 直接删除 
	if((tree[k].lson==0)&&(tree[k].rson==0)){
		tree[k].freq=0;
		int fa=tree[k].fa;
		if(tree[k].key<tree[fa].key) tree[fa].lson=0;
		else tree[fa].rson=0;
		tree[k].key=0;
		return 1;
	}

	//只有左儿子或者右儿子
	if(((tree[k].lson==0)&&(tree[k].rson!=0))||((tree[k].lson!=0)&&(tree[k].rson==0))){
		//只有左儿子
		if(tree[k].lson){
			int fa=tree[k].fa;
			if(tree[k].key<tree[fa].key)tree[fa].lson=tree[k].lson;
			else tree[fa].rson=tree[k].lson;
			tree[tree[k].lson].fa=fa;
			tree[k].key=0;
			tree[k].freq=0;
		}
		//只有右儿子
		else{
			int fa=tree[k].fa;
			if(tree[k].key<tree[fa].key)tree[fa].lson=tree[k].rson;
			else tree[fa].rson=tree[k].rson;
			tree[tree[k].rson].fa=fa;
			tree[k].key=0;
			tree[k].freq=0;
		}
		return 1;
	}

	//既有左儿子又有右儿子 此时将右儿子中key最小的结点值来替换当前要删除的结点
	int temp=searchmin(tree[k].rson);//查找右子树中最小值
	tree[k].key=temp;
	del(tree[k].rson,temp);
	return 1;
}

//查询
bool query(int k,int x)
{
	if(tree[k].freq==0)return 0;
	if(tree[k].key==x)return 1;
	if(x<tree[k].key)return query(tree[k].lson,x);
	if(x>tree[k].key)return query(tree[k].rson,x);
}

//中序遍历输出 即为升序排列值(无重复元素)
void middle_order(int k)
{
	if(tree[k].freq==0)return;
	if(tree[k].lson)middle_order(tree[k].lson);
	rep(i,tree[k].freq)printf("%d ",tree[k].key);//重复元素按重复次数输出
	if(tree[k].rson)middle_order(tree[k].rson);
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);

	scanf("%d",&n);
	tot=1;
	rep(i,n)scanf("%d",&A[i]);

	//随机化处理后 算法进行一次查询 删除等操作的平均时间为log(n) 大大优化了程序 在原始数据就是有序的情况下也能高效运行
	srand(time(0));
	int k=rand()%n;
	swap(A[0],A[k]);

	//插入操作
	rep(i,n)insert(root,A[i]);

	//删除操作
	scanf("%d",&n);
	rep(i,n){
		scanf("%d",&x);
		if(del(root,x))printf("Deleted %d\n",x);
		else printf("Not Deleted\n");
	}

	//查询操作
    scanf("%d",&n);
	rep(i,n){
		scanf("%d",&x);
		if(query(root,x))printf("Yes\n");
		else printf("No\n");
	}
	
	
    middle_order(root);//中序遍历 即为升序排序值
	
	printf("%d\n",searchmin(root));//求最小值
	printf("%d\n",searchmax(root));//求最大值


	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值