基本线段树
前言
作为一个常用的数据结构,线段树怎能不与玄学扯上关系。所以今天要讲的是:玄学。
基本概念
所谓线段树,就是将一个线段用数轴上的一个点表示。
但是我当初就没搞懂,所以我决定用我能理解的方法来讲。
想象有一条线,嗯对,一条线段。
将这条线段从中点分开,中点归左边。
就这样递归,递归,递归。
直到线段长度为1时,return;
好,这就是建树的过程。
那你肯定要问,凭什么要这样写。
别急,下面为你详细解答。
以下为重点部分:
我们都知道,数轴上的区间覆盖处理一直是个巨坑,一个不小心就超时。
所以我们想出了线段树这种辅助结构。
我们要找一个长度为1的线段时,如果用线段树,时间复杂度会是多少呢?
答案是:
我们来分析一下:
每一次递归后,待查询线段的长度就成了父亲线段长度的½
所以线段树真的是一个很高效的数据结构(某些情况下)
实现
声明:全部代码将用C++语言完成
我们用结构体来存储这棵树
struct node{
int l,r;
}tree[6*MAXN+5];
不知为何,我用4*MAXN时总会RE
先来建树部分的代码
void Build(int number,int left,int right){
tree[number].l=left;
tree[number].r=right;
if(l==r) //长度为1时
return;
int mid=(l+r)/2;
Build(number*2,left,mid); //建左子树
Build(number*2+1,mid+1,right); //建右子树
}
然后是查找部分
void Find(int number,int left,int right){
if(tree[number].l==left&&tree[number].r==right){ //如果找到了的话
printf("Find it!");
return;
}
.
.
.
// 这里并不是我偷懒,而是有问题要讨论
}
在这里我们发现了一个问题——有两种实现方式,分别是:
- 严谨认真,将左右端点依次与中点进行比较。
- 不管了,直接上,到时候不行再倒回来。
那么大家觉得哪个是对的呢?猜一猜
选1的请举手,恭喜你,你是对的
但是,它们两个都是对的,时间复杂度也没有什么区别。
而且2码起来还要简单一些。
所以说。。。
选2的请举手
原来你是大佬啊。虽然可能是蒙的
所以说,讲了一大通废话,也是时候该上代码了。
我也准备了两种代码,随大家喜好咯
方法1:
void Find(int number,int left,int right){
if(tree[number].l==left&&tree[number].r==right)
return 1;
int mid=(tree[number].l+tree[number].r)/2;
if(r<=mid)
return Find(number*2,left,right);
else if(l>mid)
return Find(number*2+1,left,right);
else return max(Find(number*2,left,mid),Find(number*2+1,mid+1,right));
}
方法2:
void Find(int number,int left,int right){
if(tree[number].l==left&&tree[number].r==right)
return 1;
if(tree[number].l>right||tree[number].r<left)
return 0;
int mid=(tree[number].l+tree[number].r)/2;
return max(Find(number*2,left,mid),Find(number*2+1,mid+1,right));
}
未完待续。。。
请关注下一篇进击的线段树——RMQ