线段树&&线段树的创建线段树的查询&&单节点更新&&区间更新
线段树
- 实现问题:常用于求数组区间最小值
- 时间复杂度:(1).建树复杂度:nlogn。(2).线段树算法复杂度:logn
什么是线段树?
- 叶子节点是原始组数arr中的元素
- 非叶子节点代表它的所有子孙叶子节点所在区间的最小值
例如:数组[2, 5, 1, 4, 9, 3]可以构造如下的二叉树(背景为白色表示叶子节点,非叶子节点的值是其对应数组区间内的最小值,例如根节点表示数组区间arr[0...5]内的最小值是1)。
线段树的创建
- 实现原理:定义包含n个节点的线段树 SegTreeNode segTree[n],segTree[0]表示根节点。那么对于节点segTree[i],它的左孩子是segTree[2i+1],右孩子是segTree[2i+2]。
const int MAXNUM = 1000;
struct SegTreeNode
{
int val;
}segTree[MAXNUM];//定义线段树
/*
功能:构建线段树
root:当前线段树的根节点下标
arr: 用来构造线段树的数组
istart:数组的起始位置
iend:数组的结束位置
*/
void build(int root, int arr[], int istart, int iend)
{
if(istart == iend)//叶子节点
segTree[root].val = arr[istart];
else
{
int mid = (istart + iend) / 2;
build(root*2+1, arr, istart, mid);//递归构造左子树
build(root*2+2, arr, mid+1, iend);//递归构造右子树
//根据左右子树根节点的值,更新当前根节点的值
segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
}
}
线段树的查询
- 已经构建好了线段树,那么怎样在它上面超找某个区间的最小值呢?查询的思想是选出一些区间,使他们相连后恰好涵盖整个查询区间,因此线段树适合解决
相邻的区间的信息可以被合并成两个区间的并区间的信息
的问题。代码如下,具体见代码解
/*
功能:线段树的区间查询
root:当前线段树的根节点下标
[nstart, nend]: 当前节点所表示的区间
[qstart, qend]: 此次查询的区间
*/
int query(int root, int nstart, int nend, int qstart, int qend)
{
//查询区间和当前节点区间没有交集
if(qstart > nend || qend < nstart)
return INFINITE;
//当前节点区间包含在查询区间内
if(qstart <= nstart && qend >= nend)
return segTree[root].val;
//分别从左右子树查询,返回两者查询结果的较小值
int mid = (nstart + nend) / 2;
return min(query(root*2+1, nstart, mid, qstart, qend),
query(root*2+2, mid + 1, nend, qstart, qend));
}
单节点更新
- 单节点更新是指只更新线段树的某个叶子节点的值,但是更新叶子节点会对其父节点的值产生影响,因此更新子节点后,要回溯更新其父节点的值。
/*
功能:更新线段树中某个叶子节点的值
root:当前线段树的根节点下标
[nstart, nend]: 当前节点所表示的区间
index: 待更新节点在原始数组arr中的下标
addVal: 更新的值(原来的值加上addVal)
*/
void updateOne(int root, int nstart, int nend, int index, int addVal)
{
if(nstart == nend)
{
if(index == nstart)//找到了相应的节点,更新之
segTree[root].val += addVal;
return;
}
int mid = (nstart + nend) / 2;
if(index <= mid)//在左子树中更新
updateOne(root*2+1, nstart, mid, index, addVal);
else updateOne(root*2+2, mid+1, nend, index, addVal);//在右子树中更新
//根据左右子树的值回溯更新当前节点的值
segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
}
区间更新
- 区间更新是指更新某个区间内的叶子节点的值,因为涉及到的叶子节点不止一个,而叶子节点会影响其相应的非叶父节点,那么回溯需要更新的非叶子节点也会有很多,如果一次性更新完,操作的时间复杂度肯定不是O(lgn),例如当我们要更新区间[0,3]内的叶子节点时,需要更新出了叶子节点3,9外的所有其他节点。为此引入了线段树中的延迟标记概念,这也是线段树的精华所在。
const int INFINITE = INT_MAX;
const int MAXNUM = 1000;
struct SegTreeNode
{
int val;
int addMark;//延迟标记
}segTree[MAXNUM];//定义线段树
/*
功能:构建线段树
root:当前线段树的根节点下标
arr: 用来构造线段树的数组
istart:数组的起始位置
iend:数组的结束位置
*/
void build(int root, int arr[], int istart, int iend)
{
segTree[root].addMark = 0;//----设置标延迟记域
if(istart == iend)//叶子节点
segTree[root].val = arr[istart];
else
{
int mid = (istart + iend) / 2;
build(root*2+1, arr, istart, mid);//递归构造左子树
build(root*2+2, arr, mid+1, iend);//递归构造右子树
//根据左右子树根节点的值,更新当前根节点的值
segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
}
}
/*
功能:当前节点的标志域向孩子节点传递
root: 当前线段树的根节点下标
*/
void pushDown(int root)
{
if(segTree[root].addMark != 0)
{
//设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递
//所以是 “+=”
segTree[root*2+1].addMark += segTree[root].addMark;
segTree[root*2+2].addMark += segTree[root].addMark;
//根据标志域设置孩子节点的值。因为我们是求区间最小值,因此当区间内每个元
//素加上一个值时,区间的最小值也加上这个值
segTree[root*2+1].val += segTree[root].addMark;
segTree[root*2+2].val += segTree[root].addMark;
//传递后,当前节点标记域清空
segTree[root].addMark = 0;
}
}
/*
功能:线段树的区间查询
root:当前线段树的根节点下标
[nstart, nend]: 当前节点所表示的区间
[qstart, qend]: 此次查询的区间
*/
int query(int root, int nstart, int nend, int qstart, int qend)
{
//查询区间和当前节点区间没有交集
if(qstart > nend || qend < nstart)
return INFINITE;
//当前节点区间包含在查询区间内
if(qstart <= nstart && qend >= nend)
return segTree[root].val;
//分别从左右子树查询,返回两者查询结果的较小值
pushDown(root); //----延迟标志域向下传递
int mid = (nstart + nend) / 2;
return min(query(root*2+1, nstart, mid, qstart, qend),
query(root*2+2, mid + 1, nend, qstart, qend));
}
/*
功能:更新线段树中某个区间内叶子节点的值
root:当前线段树的根节点下标
[nstart, nend]: 当前节点所表示的区间
[ustart, uend]: 待更新的区间
addVal: 更新的值(原来的值加上addVal)
*/
void update(int root, int nstart, int nend, int ustart, int uend, int addVal)
{
//更新区间和当前节点区间没有交集
if(ustart > nend || uend < nstart)
return ;
//当前节点区间包含在更新区间内
if(ustart <= nstart && uend >= nend)
{
segTree[root].addMark += addVal;
segTree[root].val += addVal;
return ;
}
pushDown(root); //延迟标记向下传递
//更新左右孩子节点
int mid = (nstart + nend) / 2;
update(root*2+1, nstart, mid, ustart, uend, addVal);
update(root*2+2, mid+1, nend, ustart, uend, addVal);
//根据左右子树的值回溯更新当前节点的值
segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
}
未完待续
by @Chicago_01
线段树&&线段树的创建线段树的查询&&单节点更新&&区间更新的更多相关文章
- HDU 4325 离散化+树状数组 或者 不使用树状数组
题意:给出一些花的开放时间段,然后询问某个时间点有几朵花正在开放. 由于ti<1e9,我们需要先将时间离散化,然后将时间点抽象为一个数组中的点,显然,我们需要进行区间更新和单点查询,可以考虑线段 ...
- 线段树、前缀数组:HDU1591-Color the ball(区间更新、简单题)
Color the ball Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- HDU 1556 Color the ball(线段树区间更新)
Color the ball 我真的该认真的复习一下以前没懂的知识了,今天看了一下线段树,以前只会用模板,现在看懂了之后,发现还有这么多巧妙的地方,好厉害啊 所以就应该尽量搞懂 弄明白每个知识点 [题 ...
- hihoCoder 1080 : 更为复杂的买卖房屋姿势 线段树区间更新
#1080 : 更为复杂的买卖房屋姿势 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho都是游戏迷,“模拟都市”是他们非常喜欢的一个游戏,在这个游戏里面他们 ...
- hdu 3397 Sequence operation(线段树:区间更新)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3397 题意:给你一个长度为n的0,1序列,支持下列五种操作, 操作0(0 a b):将a到b这个区间的 ...
- 【HDU 4614】Vases and Flowers(线段树区间更新懒惰标记)
题目0到n-1的花瓶,操作1在下标a开始插b朵花,输出始末下标.操作2清空[a,b]的花瓶,求清除的花的数量.线段树懒惰标记来更新区间.操作1,先查询0到a-1有num个空瓶子,然后用线段树的性质,或 ...
- HDU 5023 A Corrupt Mayor's Performance Art(线段树区间更新)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5023 解题报告:一面墙长度为n,有N个单元,每个单元编号从1到n,墙的初始的颜色是2,一共有30种颜色 ...
- HDU 4902 Nice boat 2014杭电多校训练赛第四场F题(线段树区间更新)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4902 解题报告:输入一个序列,然后有q次操作,操作有两种,第一种是把区间 (l,r) 变成x,第二种是 ...
- hdu 1556:Color the ball(线段树,区间更新,经典题)
Color the ball Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
随机推荐
- [转]Configure Network Drive Visible for SQL Server During Backup and Restore Using SSMS
本文转自:https://mytechmantra.com/LearnSQLServer/Configure-Network-Drive-Visible-for-SQL-Server-During-B ...
- Js四则运算精度问题处理
JavaScript小数在做四则运算时,精度会丢失,这会在项目中引起诸多不便.先看个具体的例子: //较小的数运算 console.log(0.09999999 + 0.00000001); //0. ...
- 常用js-API
jquery.base64.js //下面是64个基本的编码 var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm ...
- 宜立方商城中,mvn报错'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique: org.springframework:spring-webmvc:jar报错
'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique: org.springframework:s ...
- vue + element ui 表格自定义表头,提供线上demo
前言:工作中用到 vue+element ui 的前端框架,需要使用自定义表头,需要使用 re.转载请注明出处:https://www.cnblogs.com/yuxiaole/p/9710826.h ...
- 传统javabean与spring中的bean的区别
javabean已经没人用了 springbean可以说是javabean的发展, 但已经完全不是一回事儿了 用处不同:传统javabean更多地作为值传递参数,而spring中的bean用处几乎无处 ...
- CSS 高度(css height)
DIV+CSS height高度知识教程篇 DIV CSS高度简介这里的CSS高度是指通过CSS来控制设置对象的高度.使用CSS属性单词height.单位可以使用PX,em等常用使用PX(像素)为ht ...
- Hadoop总结
背景 Hadoop是一个由Apache基金会所开发的分布式系统基础架构.用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力进行高速运算和存储. Mapreduce1 vs YA ...
- Android设备网络、屏幕尺寸、SD卡、本地IP、存储空间等信息获取工具类
Android设备网络.屏幕尺寸.SD卡.本地IP.存储空间.服务.进程.应用包名等信息获取的整合工具类. package com.qiyu.ddb.util; import android.anno ...
- Retrofit+RxJava(2)-基本使用
首先是抽象的基类 public abstract class BaseApi { public static final String API_SERVER = "服务器地址" p ...