初步认识线段树(1)线段树用途+建树

初步线段树

                                    ——HM
 

        当你遇到了这样的题目时:

现在请求你维护一个数列,要求提供以下两种操作:
1、 查询操作。
语法:Q L
功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。
限制:L不超过当前数列的长度。(L>=0)
2、 插入操作。
语法:A n
功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。
限制:n是整数(可能为负数)并且在长整范围内。
注意:初始时数列是空的,没有一个数。

 

你的第一反应是什么? 当然是暴力模拟了。

然后我们来分析一下数据:操作个数<=20,0000,暴力做的话,时间复杂度O(m^2),绝对TLE,可能连40都达不到。

于是线段树横空出世了。

 

 

线段树是一种二叉查找树,充分利用了二叉树“快”的特点,可以高效率完成区间修改、区间查询、区间最值等操作。

大体思路是:将一个区间划分为一些单元区间,每个单元区间对应线段树中的一个叶节点。其有以下几种操作:

① 区间修改(change(x,tree)):将ai~aj全部加x。

② 删除元素(Delete(x,tree)):从线段树tree中删除元素x。

③ 查找元素(Search(x,tree)):返回树中元素x的地址指针。

 速度都为O(logn)

 

当然还有一些更加实用的操作,但稍微有些复杂,会举例详细讲解。

 

线段树的构造:线段树大多都是用结构体递归来构造的,代码如下:

 

#include 
using namespace std;

struct Node{
	int Left, Right; Node *LeftChild, *RightChild;//树节点机构体,体现递归 
};

void build(Node *cur, int L, int r);

int main()
{
	Node *root;
	root=new Node;//一定要new
	int L,r;
	cin>>L>>r;
	
	build(root,L,r);
	
	return 0;
}

void build(Node *cur, int L, int r)
{
	cur->Left = L;cur->Right = r;
	if (L!=r){
		cur->LeftChild=new Node;
		 cur->RightChild = new Node;
		 build(cur->LeftChild,L,(L+r)/2);//递归实现建树
		 build(cur->RightChild,(L+r)/2+1, r);//同上 
	}
	else{
		cur->LeftChild=NULL;
		cur->RightChild=NULL;//停止递归
	}
}

这样,一个线段树便建成了。

参考一些《算法竞赛》的内容。

你可能感兴趣的:(数据结构专栏)