线段树&&线段树的创建线段树的查询&&单节点更新&&区间更新
线段树
- 实现问题:常用于求数组区间最小值
- 时间复杂度:(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 ...
随机推荐
- Golang 并发Groutine实例解读(二)
go提供了sync包和channel机制来解决协程间的同步与通信. 一.sync.WaitGroup sync包中的WaitGroup实现了一个类似任务队列的结构,你可以向队列中加入任务,任务完成后就 ...
- Golang 并发Groutine实例解读(一)
Go语言的并发和并行 不知道你有没有注意到一个现象,还是这段代码,如果我跑在两个goroutines里面的话: var quit chan int = make(chan int) func loop ...
- CSS阴影盒子
box-shadow:inset 横向偏移量 纵向偏移量 模糊范围 扩展范围 阴影颜色: 1.inset :(可选) 内阴影,不设置inset,表示盒子外部显示一个投影.也可写在最后. 2.横 ...
- 如鹏网学习笔记(四).Net常用类库
.Net常用类库 一.String成员方法(常用) 1,bool Contains(string str) 判断字符串对象是否包含给定的内容 2,bool StartsWith(String str) ...
- 修改MVC视图默认搜索规则(IViewEngine)
前几天我自己在写一个系统,写到后台管理系统的时候,我突然有个想法就是:想在区域视图下新建文件,单独处理后台一些业务:Area/AdminManager/View/Content/Index.cshtm ...
- static关键字的内存分析
通常情况下,Java把内存分为栈内存.堆内存和方法区 栈内存用来存放一些基本类型的变量和数组(数组也是一种引用类型)及对象的引用变量 堆内存主要是来放置对象的,即我们在程序中new出来的对象. sta ...
- 二:Jquery-action
一:dom对象和jq对象 1.对象含义: dom对象:js方法获取元素,将dom对象存储在变量中 jq对象:jq方法获取元素的jq对象,将jq对象存储在变量中 相互之间不能使用另外一个对象的任何属性和 ...
- Hystrix使用详解
原文参考:http://hot66hot.iteye.com/blog/2155036 一:为什么需要Hystrix? 在大中型分布式系统中,通常系统很多依赖(HTTP,hession,Netty,D ...
- IDEA下的第一个springBoot
1.第一步打开File->New->Project,SDK根据自己的需要选择,我这边选的是java7 2.Next之后 设置group 和artifact,根据自己的需要进行修改. 3.导 ...
- 移除button点击时的黑边
input[type=submit], input[type=reset], input[type=button]{ outline:none; filter: chroma(color=#00000 ...