基本线段树

前言

作为一个常用的数据结构,线段树怎能不与玄学扯上关系。所以今天要讲的是:玄学。

基本概念

所谓线段树,就是将一个线段用数轴上的一个点表示。
但是我当初就没搞懂,所以我决定用我能理解的方法来讲。
想象有一条线,嗯对,一条线段。
将这条线段从中点分开,中点归左边。
就这样递归,递归,递归。
直到线段长度为1时,return;
好,这就是建树的过程。
那你肯定要问,凭什么要这样写。
别急,下面为你详细解答。


以下为重点部分:
我们都知道,数轴上的区间覆盖处理一直是个巨坑,一个不小心就超时。
所以我们想出了线段树这种辅助结构。
我们要找一个长度为1的线段时,如果用线段树,时间复杂度会是多少呢?
答案是:

O(logn)

我们来分析一下:
每一次递归后,待查询线段的长度就成了父亲线段长度的½
所以线段树真的是一个很高效的数据结构(某些情况下)


实现

声明:全部代码将用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. 不管了,直接上,到时候不行再倒回来。

那么大家觉得哪个是对的呢?猜一猜


选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

posted @ 2018-02-23 15:41  SteinGate  阅读(189)  评论(0编辑  收藏  举报