『数据结构』线段树

线段树原理

线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为\(O(logn)\)。

线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是\([a,b]\),那么\((c=(a+b)/2)\)左儿子的区间是\([a,c]\),右儿子的区间是\([c+1,b]\)。

下面我们从一个经典的例子来了解线段树,问题描述如下:从数组a[0...n-1]中查找某个数组某个区间内的最小值,其中数组大小固定,但是数组中的元素的值可以随时更新。

对这个问题一个简单的解法是:遍历数组区间找到最小值,时间复杂度是\(O(n)\),额外的空间复杂度\(O(1)\)。当数据量特别大,而查询操作很频繁的时候,耗时可能会不满足需求。

另一种解法:使用一个二维数组来保存提前计算好的区间\([i,j]\)内的最小值,那么预处理时间为\(O(n^2)\),查询耗时\(O(1)\), 但是需要额外的\(O(n^2)\)空间,当数据量很大时,这个空间消耗是庞大的,而且当改变了数组中的某一个值时,更新二维数组中的最小值也很麻烦。

我们可以用线段树来解决这个问题:预处理耗时\(O(n)\),查询、更新操作\(O(logn)\),需要额外的空间\(O(n)\)。根据这个问题我们构造如下的二叉树

叶子节点是原始组数\(a\)中的元素

非叶子节点代表它的所有子孙叶子节点所在区间的最小值

例如对于数组\([2, 5, 1, 4, 9, 3]\)可以构造如下的二叉树(背景为白色表示叶子节点,非叶子节点的值是其对应数组区间内的最小值,例如根节点表示数组区间\(a[0...5]\)内的最小值是1):

由于线段树的父节点区间是平均分割到左右子树,因此线段树是完全二叉树,对于包含\(n\)个叶子节点的完全二叉树,它一定有\(n-1\)个非叶节点,总共\(2n-1\)个节点,因此存储线段是需要的空间复杂度是\(O(n)\)。那么线段树的操作:创建线段树、查询、节点更新 是如何运作的呢(以下所有代码都是针对求区间最小值问题)?

对于线段树我们可以选择和普通二叉树一样的链式结构。由于线段树是完全二叉树,我们也可以用数组来存储,下面的讨论及代码都是数组来存储线段树,节点结构如下(注意到用数组存储时,有效空间为\(2n-1\),实际空间确不止这么多,比如上面的线段树中叶子节点\(1\)、\(3\)虽然没有左右子树,但是的确占用了数组空间,实际空间是满二叉树的节点数目。

线段树的代码实现

建线段树的过程

void build(int l, int r, int root)
{
    if (l==r)
    {
        sum[root]=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,root<<1);
    build(mid+1,r,root<<1|1);
    pushup(root);
}

pushup过程

void push(int root)
{
    sum[root]=sum[root<<1]+sum[root<<1|1];
}

查询线段树

int query(int ansl, int ansr, int l, int r,int root)
{
    if (ansl<=l && r<=ansr) return sum[root];
    int mid=(l+r)>>1;
    int ans=0;
    if (ansl<=mid) ans+=query(ansl,ansr,l,mid,root<<1);
    if (ansr>mid) ans+=query(ansl,ansr,mid+1,r,root<<1|1);
    return ans;
}

单节点更新

void update(int pos, int c, int l, int r, int root)
{
    if (l==r)
    {
        sum[root]+=c;
        return;
    }
    int mid=(l+r)/2;
    if (pos<=mid) update(pos,c,l,mid,root<<1); else update(pos,c,mid+1,r,root<<1|1);
    pushup(root);
}

原文地址:https://www.cnblogs.com/shenxiaohuang/p/10162292.html

时间: 2024-08-04 07:31:08

『数据结构』线段树的相关文章

《数据结构》线段树入门(二)

今天继续介绍——线段树之延迟标记 接上期<数据结构>线段树入门(一):http://www.cnblogs.com/shadowland/p/5870339.html 在上期介绍了线段树的最基本内容(线段树单点修改,区间查询),这次将介绍:区间修改,区间查询. Question: 给你N个数,有两种操作: 1:给区间[a,b]的所有数增加X 2:询问区间[a,b]的数的和. 输入描述: 第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,每行表示操作的个数,如果第一数是1,后接3个正

《ACM/ICPC 算法训练教程》读书笔记 之 数据结构(线段树详解)

依然延续第一篇读书笔记,这一篇是基于<ACM/ICPC 算法训练教程>上关于线段树的讲解的总结和修改(这本书在线段树这里Error非常多),但是总体来说这本书关于具体算法的讲解和案例都是不错的. 线段树简介 这是一种二叉搜索树,类似于区间树,是一种描述线段的树形数据结构,也是ACMer必学的一种数据结构,主要用于查询对一段数据的处理和存储查询,对时间度的优化也是较为明显的,优化后的时间复杂为O(logN).此外,线段树还可以拓展为点树,ZWK线段树等等,与此类似的还有树状数组等等. 例如:要将

《数据结构》线段树入门(一)

今天介绍一种非常特殊的数据结构——线段树 首先提出一个问题: 给你n个数,有两种操作: 1:给第i个数的值增加X 2:询问区间[a,b]的总和是什么? 输入描述 输入文件第一行为一个整数n,接下来是n行n个整数,表示格子中原来的整数.接下一个正整数q,再接 下来有q行,表示q个询问,第一个整数表示询问代号,询问代号1表示增加,后面的两个数x和A表示给 位置X上的数值增加A,询问代号2表示区间求和,后面两个整数表示a和b,表示要求[a,b]之间的区间和. 样例输入 4 7 6 3 5 2 1 1

【数据结构】线段树 (定义 &amp; 点修改/区间查询)

[本文描述高级数据结构线段树的定义] [并解决 点修改/区间查询 的问题] 结构与定义 线段树的基本结构 ? 由图可知,线段树的每一个节点都代表着一段区间 且同一层的节点(深度相同的节点)所表示的区间互不重叠 所有叶子节点代表的区间左边界与右边界相同(叶子节点代表单个元素) 普遍规定 如果某个非叶子节点代表的区间包含元素个数为奇数 则它的左儿子包含的元素个数比右儿子大 1 在代码部分,非叶子节点表示区间为 [l,r] 则左儿子为 [ l , (l+r)/2 ] ,右儿子为 [ (l+r)/2+1

数据结构2——线段树

一.相关介绍 线段树:它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题.由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn). 线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b]. 下面我们从一个经典的例子来了解线段树,问题描述如下: 从数组arr[0...n-1]中查找某个(子)数组内的最小值,其中数组大小固定,但是数组中

数据结构之线段树

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]. 因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度. 用线段树解题关键是要清楚的知道每个节点要存放什么信息,以及这些节点如何高效的更新,维护,查询,不一定每次都更新到叶子节点,这样复杂度最坏有可能是0(n) 下面附上一道题 poj3264

【数据结构】线段树入门

线段树是一种二叉搜索树. 它将一个区间划分成一些子区间,每个子区间对应线段树中的一个叶节点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)>>1],右儿子表示的区间为[(a+b)>>1+1,b].也就是说线段树是一棵平衡二叉树. 下图是对于[1,10]的区间构造的一棵线段树. 线段树的基本操作函数有三个. 分别是build(建树),update(更新),query(查询区间和). 线段树定义: struct node{ int left,righ

数据结构(线段树):BZOJ 1568 [JSOI2008]Blue Mary开公司

1568: [JSOI2008]Blue Mary开公司 Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 602  Solved: 214[Submit][Status][Discuss] Description Input 第 一行 :一个整数N ,表示方案和询问的总数. 接下来N行,每行开头一个单词“Query”或“Project”. 若单词为Query,则后接一个整数T,表示Blue Mary询问第T天的最大收益. 若单词为Project,则

【数据结构】线段树(interval tree)

线段树(interval tree),也叫区间树.也是一种二叉搜索树,同一般的BST不同之处在于:线段树的每一个结点包含的是一个区间而不是一个数.具体的描述如下: 从图上可以看出,线段树的每一个结点都是一个线段(区间),子节点是对父结点的进一步分划,每个子节点的长度都是父节点的二分,每个叶子结点就是一个元素. 每个节点可以用一个变量hit_count来计算在每一段的命中率,这样可以用来统计此线段线段或者区间内的命中率. 区间树主要用在一些跟统计和分部相关的计算中,可以快速找到相应的数据.