线段树学习(一)

  看到UESTC的数据结构专题快要结束了,感觉自己真心浪费了好多时间,没有像鑫航学姐那样叮嘱的一样,紧紧的跟住训练。

所以下决心认认真真的开始学习下线段树的知识,以前对于线段树的学习都是一知半解的,就是说,我只知道线段树是用来单点更新和区间查值的,其实,线段树的功能远远不止这些。

  先来说下,线段树是用来求解有关区间问题的绝逼法宝,为什么说绝逼呢。因为它能够在logn的时间内完成每次的查询。

查询的分类:

  1.区间查询

  -访问某段区间的某些性质(比如说,最大值,最小值,连续和,等等)

  2.区间更新

  -某些操作影响了某段区间(比如说,统一加一个值)

归于以上两个查询,我们总结出了线段树能够解决的以下三个问题.

  -更新点,查询区间

  -更新区间,查询点

  -更新区间,查询区间

要想深入的理解线段树,我们从一个比较经典的例子开始了解吧。

我现在有一个长度为 n的一维数组(a[1]~a[n])

我们每次对这个数组只允许有以下的操作:

~1.修改数组中某个元素的值

   - [1,5,4,1,6]  ---(a[2] = 3)---[1,3,4,1,6]

~2.询问数组中某段区间的最大值

   -[1,5,4,1,6]   ---(max(1,4)=?) ---> 5

~3.询问数组中某段区间的和

 -[1,5,4,1,6]  ---(sum(3,5)=?) ---> 11

  在没有知道线段树之前,我们知道,要对一个数组进行这样的操作是很简单的每次只要O(n)的时间扫一遍就是可以得到各种询问的ans的。

那么,如果我要进行Q次询问该怎么办呢?这样的话就变成了O(nQ)的复杂度了,,擦,,这也太慢了吧,随便给你一组数据,你就T了

所以,为了在更快的时间里解决这些查询问题,我们引入了一个叫做线段树的数据结构,线段树就是一个能在logn的时间内完成每次操作。

线段树的本质是一棵二叉树,不同于一般的二叉树来说,线段树的每个节点维护的都是一段区间的信息。

比如下面这个二叉树,对于一个长度为5的数组a[1]~a[5]

       [1,.5]

    [1,3]      [4,5]

  [1,2]   [3,3]   [4,4]  [5,5]                          对于这个树的构建有几点说明,对于任意一个非叶子节点来说,如果它所维护的区间是[l,r]的话,它的左儿子区

[1,1]   [2,2]                间就是[l,(l+r)/2], 右儿子区间就是[(l+r)/2+1,r];

线段树      ---实现

每个节点记录的信息

1 struct Segtree
2 {
3     int left,right;//区间的端点
4     int mx,mn;//区间内的最大值和最小值
5     int sum;//区间内元素的总和
6 }tree[MAX*4];

  我们先用一个数组a[]来记录节点,并且根节点的下标为1,然后,对于任意一个节点来说a[k]来说,他的左儿子是a[k<<1],右儿子是a[k<<1|1]

建立一棵线段树代码实现

void build ( int id,int l,int r )
{
    tree[id].left = l; tree[id].right = r;
    if ( l==r )
    {
        tree[id].sum = tree[id<<1].sum+tree[id<<1|1].sum;
        tree[id].mx = max(tree[id<<1].mx,tree[id<<1|1].mx);
        tree[id].mn = min(tree[id<<1].mn,tree[id<<1|1].mn);
    }
    else
    {
        int mid = (l+r)>>1;
        build(id<<1,l,mid);
        build(id<<1|1,mid+1,r);
        tree[id].sum = tree[id<<1].sum+tree[id<<1|1].sum;
        tree[id].mx = max(tree[id<<1].mx,tree[id<<1|1].mx);
        tree[id].mn = min(tree[id<<1].,mn,tree[id<<1|1].mn);
    }
}
原文地址:https://www.cnblogs.com/wikioibai/p/4452177.html