线段树实现动态查询和删节点

本文介绍了如何使用线段树解决动态查询第K大数并支持节点删除的问题。首先给出了常规解法,然后详细阐述了线段树的结构,包括节点定义、构建、添加数和查找删除操作。示例代码展示了线段树在动态查找和删除过程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:
有一个未知排列,已知排列中元素个数以及每个元素前面有多少个元素比它本身小。
求此排列。
/*  分析:
*  解法一:全排列 然后挑选符合题意的排列
*  解法二:倒过来看 
*   最后一个元素n[i] 代表这个元素在整个排列的位置排在s[i]+1个
*   倒数第二个元素m[i-1] 代表这个元素在剩下的排列中排在s[i-1]+1个
* 依次类推
*   有:看代码..
*/

全排列就不写了,主要放解法二代码:
/****** version 1************/

#include <cstdio>
#include <cstdlib>
#include <iostream>

#define maxn 10010
#define finput(file) freopen(file,"r",stdin)
#define foutput(file) freopen(file,"w",stdout) 
using namespace std;


int main(int argc, char *argv[])
{
	int num,i,j,k;
	int ans[maxn],s[maxn],n[maxn];

//	finput("in.txt");
//	foutput("out.txt");

	cin>>num;
	i=0;
	while(i++<num)
	{
		cin>>s[i];
		ans[i]=0;
		n[i]=0;
	}
	for(i=num;i>0;i--)//处理剩下排列的最后一个
	{
		for(j=1,k=0;k<s[i]+1;j++)
		{
			if(n[j]==0)
				k++;
		}
		ans[i]=j-1;
		n[j-1]=1;
	}
	for(i=num;i>=0;i--)
		cout<<ans[i]<<" ";
	return 0;
}




这是常规解法。下面是线段树解。
/*****  version 2 ************/
/****线段树解*****/
/*实现在一个集合中查找第K大数而且还要支持动态删除点*/
/*动态删除的时候。。设置一个跳转的值。。*/

#include <cstdio>
#include <cstdlib>
#include <iostream>

#define maxn 10010
#define finput(file) freopen(file,"r",stdin)
#define foutput(file) freopen(file,"w",stdout) 
using namespace std;

typedef struct node
{
	int sta,end;
	int leftnum;
	struct node *leftChild, *rightChild;
	int number;
}node;

node* buildTree(int a,int b)
{
	node* p ;
	p = (struct node*)malloc(sizeof(node));
	if(a == b) 
	{
		p->leftChild = NULL;
		p->rightChild = NULL;
		return p;
	}
	p->sta = a;
	p->end = b;
	
	p->leftChild = buildTree(a,(a+b)/2);
	p->rightChild = buildTree((a+b)/2+1,b);
	p->leftnum = (b-a)/2+1;//左孩子个数
	//左孩子个数是(b-a+1)/2会错  

	return p;
};

//第a大的值赋为num
void addNumber (node* p,int a,int num)
{
//	if(a > (p->leftnum + p->rightnum))
//		return;
	if(p->leftChild == NULL)
	{
//		cout<<"t1:-"<<a<<endl; //a必定为1
		p->number = num;
		return;
	}
	if(a > p->leftnum)
		addNumber(p->rightChild, a - p->leftnum,num);
	if(a <= p->leftnum)
		addNumber(p->leftChild, a, num);
};

//查找第N大的数,并删除
int search(node *p, int a)
{
	int res;
	if(p->leftChild == NULL)
	{
		return p->number;
	}
	if(a > p->leftnum)
		res = search(p->rightChild, a-p->leftnum);
	if(a <= p->leftnum)
	{
		p->leftnum--;
		res = search(p->leftChild, a);
	}
	return res;
}

int main(int argc, char* argv[])
{
	node *tree;
	int casenum;
	int num,a[maxn],ans[maxn];

//	finput("in.txt");

	cin>>casenum;
	while(casenum--)
	{
		cin>>num;
		tree = buildTree(1, num);
		int i,j=0;
		for(i=1;i<=num;i++)
		{
			cin>>a[i];
			addNumber(tree,i,i);
		}
		for(i=num;i>0;i--)
			ans[i]=search(tree, a[i]+1);
		for(i=1;i<=num;i++)
			cout<<ans[i]<<" ";
		cout<<endl;
	}
	return 0;
}




   线段树一般都是操作静态数据,针对这个题目,每查询一次就要将所进行的查询结果从原集合中删除
于是,设置一下左子树所拥有的数目,就可以进行选择了。因为区间[a,b]是我们操作的,所以能保证不超,所以非左即右。
   针对每一个节点,都有一个左孩子数目。所以我们能很方便找到区间内第k大。
  描述不是很清楚》。《 主要看代码吧..  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值