hdu1166--简单线段树

线段树是一种数据结构,在查找删除等操作时能节省很多时间,大约为log(n)。但值得注意的是,在建树时所花费的时间比较多,远大于log(n);

做了几道简单的线段树的题目,现在以hdu的1166敌兵布阵为例:

题意:给出一个整型数组a[ ],两种操作:

1.增加(Add)或减少(Sub) a[ i ] 的值;

2.(Query)计算a[ i ] 到 a[ i+k ] 的值(累加和),并输出; 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define max 50005
struct node
{
	int r,ldata;    //l表示最左端,r表示最右端,data表示数值
};
struct node tree[3*max];   //通常数组开到3*n就够了

int a[2*max];

//1.用指针,但是代码冗长,而且需要注意的细节较多,不建议。
//2.用数组来构造,虽然消耗空间大,但是简单易懂,建议平时都用数组。
void creat(int num,int le,int ri) 
{
	int mid;
	tree[num].l=le; //使父节点的范围为le至ri
	tree[num].r=ri;
	if(le==ri){
		tree[num].data=a[ri];
		return ;
	}
	mid=(le+ri)/2;
	creat(num*2,le,mid);				//建立左子树
	creat(num*2+1,mid+1,ri);			 //建立右子树
	
	//父节点的data为其左右子树的data之和...此位置语句比较灵活,根据题意设置父节点的data与左右 
	//子树data的关系..有时可以其最值,有时可以其他情况

	tree[num].data=tree[2*num].data+tree[2*num+1].data;
}

//查找,在这到题中以查找到区间时结束,即 le=a[i] to ri=a[i+k]..
//有时可能必须查找到叶子节点才能结束
 
int search(int num,int le,int ri)

{
	int mid;
	if(tree[num].l==le&&tree[num].r==ri)	 //找到时输出data			
		return tree[num].data;
	
	mid=(tree[num].l+tree[num].r)/2;
	if(ri<=mid)                                    //画图判断比较清晰。有三种情况:1.当要查找的区间均在此节点tree[num]的中间偏左时,则找其左子树
		return search(2*num,le,ri);
	else if(le>mid)                                  //2.当要查找的区间均在此节点tree[num]的中间偏右时,则找其右子树
		
		return search(2*num+1,le,ri);
	else                                              //3.当要查找的区间既在此节点tree[num]的中间左右都有时,则找
	{ 
		int t1=search(2*num,le,mid);             //找左子树从le to  mid 这段区间
		int t2=search(2*num+1,mid+1,ri);         //找右子树从mid+1 to ri 这段区间
		return t1+t2;                            //记得最后要返回其左右子树data之和,因为求的时候一直递归到最后一层,所以要有返回值一直回到最初寻							 //找的区间  
	}
}

void insert(int num,int le,int k)                       //插入,题目要求单点插入,有时也会有区间插入.插入类似查找,因为必须找到才能更新数值,所以有时可以							//合在一起写
{
	int mid;	
	if(tree[num].l==tree[num].r)                    //单点插入时,需要找到叶子结点
	{
		if(tree[num].l==le)                     //加入叶子结点==要找的a[i],则更新数值
			tree[num].data+=k;
		return ;	
	}
	
	mid=(tree[num].l+tree[num].r)/2;
	
	if(le<=mid)
		insert(2*num+1,le,k);                  //由于查找单点,所以只需判断该点位于当前节点tree[num]的mid左或右端即可
											   //如果查找的时区间,则应该跟查找是判断一样,分三种情况
	if(le>=mid)
		insert(2*num,le,k);
	tree[num].data+=k;                             //由于是递归,所以不要忘记在每次最后更新值.以便返回父亲节点
}

int main()
{
	int t,n,i,b,s,j;
	char ch[10];
	scanf("%d",&t);
	for(j=1;j<=t;j++)
	{
		//	 Init(); 
		scanf("%d",&n);
		for(i=1;i<=n;i++)
			scanf("%d",&a[i]);
		creat(1,1,n);
		printf("Case %d:\n",j);
		while(scanf("%s",ch)&&strcmp(ch,"End")!=0)
		{
			scanf("%d%d",&s,&b);
			if(strcmp(ch,"Query")==0)
				printf("%d\n",search(1,s,b));
			
			if(strcmp(ch,"Add")==0)
				insert(1,s,b);
			if(strcmp(ch,"Sub")==0)
				
				insert(1,s,-b);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值