- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
声明( 叠甲 ):鄙人水平有限,本文为作者的学习总结,仅供参考.
线段树说是算法,更应该算是一种二叉树数据结构的使用。 其每个树的节点表示一个区间,其孩子节点表示该区间二分下来的两个节点,其值可以表示这个区间数据的某种运算,如最值、求和等,以下以数组 [1,2,3,4] 为栗子说明,如下所示,根节点表示区间 [1,4] 的和,其它以此类推.
node:当前区间数的和[区间的左边界,区间的右边界]
10[1,4]
/ \
3[1,2] 7[3,4]
/ \ / \
1[1] 2[2] 3[3] 4[4]
有如上所示的二叉树以后我们获取区间和的时间复杂度就从 O(n) 到了 O(logn),但数据量十分庞大时这是十分重要的。当然,在节点维护时需要使用一种特殊的方法进行 —— Lazy-tag 技术,这让修改的和时间复杂为降为了O(logn).
上面说过,线段树是二叉树的一种,故在深入线段树时,我们先来了解一下二叉树的一些知识点:
如下编号为 K 的节点对应的左孩子为 K+K,右孩子为 K+K+1 在程序为了提高运行效率常常写成 K<<1 与 K<<1|1 。
node:节点编号
K
/ \
K<<1 K<<1|1
对于线段树来说,Lazy-tag 技术是十分的重要的,这是将时间复杂减小来的原因。 其实现的方法具体来说就是使用一些数来对节点进行标记,从而使只有对应区间的根节点会被进行更改,不其内部的值不做更改,具体代码实现见下文.
如题,已知一个数列,你需要进行下面两种操作:
这种要多次对不同的区间进行操作,线段树是很好的选择,其代码实现可以分为以下几个步骤 。
如论是维护还是查询我们都应该先有一个对应的目标不是 。
// 创建一个开始编号为 index
// 区间为 [l,r] 的一个线段树
void build_tree(int index,int l,int r)
{
// 如果为叶节点,即区间中自有一个数
if(r == l)
{
tree[index] = nums[l];
return;
}
// 递归遍历所有的节点
int m = (r+l) >> 1; // 二分区间
build_tree(index<<1,l,m);// 左孩子
build_tree(index<<1|1,m+1,r);// 右孩子
// 赋值,父节点值为其俩孩子的和
tree[index] = treep[index<<1] + treep[index<<1|1];
}
在维护数据时,我使用 Lazy-tag 的方法进行处理,具体步骤如下:
【1】 判断区间 [l,r] 是否在 [x,y] 内 【2】 根据该节点是否被标记来确定是否要进行 lazy-tag的下传,通常使用push_down函数来实现 【3】判断是否进行左右节点的递归 【4】更新父节点的数据 。
// 父节点的 lazy-tag 向其孩子进行传递
void push_down(int index,int l,int r)
{
int m = (l+r)>>1;
// 左孩子
tree[index<<1] += tag[index]*(m-l+1);
tag[index<<1] += tag[index];
// 右孩子
tree[index<<1|] += tag[index]*(r-m);
tag[index<<1|] += tag[index];
// 去除父节点的标志
tag[index] = 0;
}
// 对编号为 index,区间 [l,r] 的中 [x,y] 进行修改
void update(int index,int l,int r,int x,int y)
{
// [1] 判断区间 [l,r] 是否在 [x,y] 内
if(x <= l && y >= r)
{
tree[index] += k*(r-l+1);
tag[index] += k;
return;
}
// [2] 根据该节点是否被标记来确定是否要进行 lazy-tag的下传,通常使用push_down函数来实现
if(tag[index] != 0) push_down(index,l,r);
// [3] 判断是否进行左右节点的递归
int m = (l+r)>>1;
if(x <= m) update(index,l,m,x,y); // 左边
if(y > m) update(index,m+1,r,x,y);// 右边
// [4] 更新父节点的数据
tree[index] = treep[index<<1] + treep[index<<1|1];
}
需要注意的是,查询时也需要进行 Lazy-tag 的下传 。
// 查询 [l,r] 中的 [x,y] 区间
ll calc(int index,int l,int r,int x,int y)
{
// [1] [l,r]是否被[x,y]覆盖
if(x <= l && y >= r)
{
return tree[index];
}
// [2] lazy-tag 下传
if(tag[index] != 0)
push_down(index,l,r);
// [3] 递归左右孩子节点,并计算结果
ll ret = 0;
int m = (l+r)>>1;
if(x <= m) ret += calc(index<<1,l,m,x,y); // 左边
if(y > m) ret += calc(index<<1|1,m+1,r,x,y); // 右边
return ret;
}
#include <bits/stdc++.h>
using namespace std;
#define ll long long int
#define N_MAX 100000
int n,m,k;
ll nums[N_MAX+1],tree[N_MAX*4+1],tag[N_MAX*4+1];
// 建树
void build_tree(int index,int l,int r)
{
// 初始化标记
tag[index] = 0;
// 如果是叶节点
if(l == r)
{
tree[index] = nums[l];
return;
}
// 递归遍历所有节点
int m = (l+r) >> 1;
build_tree(index<<1,l,m); // 左孩子
build_tree(index<<1|1,m+1,r);// 右孩子
// 父节点的值为两孩子
tree[index] = tree[index<<1] + tree[index<<1|1];
}
// lazy-tag 下传
// 需要对左右孩子的 tag 与值都进行修改
void push_down(int index,int l,int r)
{
int m = (l+r)>>1;
// 左孩子
tag[index <<1] += tag[index];
tree[index<<1] += tag[index]*(m-l+1);
// 右孩子
tag[index <<1|1] += tag[index];
tree[index<<1|1] += tag[index]*(r-m);
// 清除自己的标志
tag[index] = 0;
}
// 更新线段树节点的数据
void update(int index,int l,int r,int x,int y)
{
// [1] [l,r]是否被[x,y]覆盖
if(x <= l && y >= r)
{
// 更新数据与 lazy-tag
tree[index] += k*(r-l+1);
tag[index] += k;
return;
}
// [2] lazy-tag 下传
if(tag[index] != 0)
push_down(index,l,r);
// [3] 递归左右孩子节点
int m = (l+r)>>1;
if(x <= m) update(index<<1,l,m,x,y); // 左边
if(y > m) update(index<<1|1,m+1,r,x,y); // 右边
// [4] 更新数据
tree[index] = tree[index<<1] + tree[index<<1|1];
}
// 查询
ll calc(int index,int l,int r,int x,int y)
{
// [1] [l,r]是否被[x,y]覆盖
if(x <= l && y >= r)
{
return tree[index];
}
// [2] lazy-tag 下传
if(tag[index] != 0)
push_down(index,l,r);
// [3] 递归左右孩子节点,并计算结果
ll ret = 0;
int m = (l+r)>>1;
if(x <= m) ret += calc(index<<1,l,m,x,y); // 左边
if(y > m) ret += calc(index<<1|1,m+1,r,x,y); // 右边
return ret;
}
void print_tree()
{
cout << "tree: ";
for(int i = 1;i <= 7;i++)
{
cout << tree[i] << " ";
}
cout << endl;
}
int main()
{
cin >> n >> m;
// [1] 获取数据并进行建树
for(int i = 1;i <= n;i++)
{
cin >> nums[i];
}
build_tree(1,1,n);
while(m--)
{
int x,y,op;
cin >> op >> x >> y;
if(op == 1) // 更新数据
{
cin >> k;
update(1,1,n,x,y);
}
else // 搜索数据
{
cout << calc(1,1,n,x,y) << endl;
}
}
}
洛谷线段树题解 木子喵的算法课 线段树的懒标记与应用 本文到此结束,希望对您有所帮助.
最后此篇关于算法总结--线段树的文章就讲到这里了,如果你想了解更多关于算法总结--线段树的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
有没有办法将多个线段视为 1 条线? IE:我将鼠标悬停在其中一个上,两者都会突出显示,并且切换图例中的可见性将隐藏这两个部分。 http://jsfiddle.net/rayholland/HSvB
有没有办法将多条线段视为一条线? IE:我将鼠标悬停在一个上,两个都突出显示,切换图例中的可见性将隐藏两个部分。 http://jsfiddle.net/rayholland/HSvBj/2/ ser
我正在尝试解决有关使用箭头键绘制线条的练习。当按下任一箭头键时,该线从中心开始向东、西、北或南绘制。该代码仅在东或西方向有效,而在北或南方向无效,这是我的问题!! 有人可以给我关于这件事的想法吗?谢谢
给定每条线的起点和终点的 XYZ 坐标,如何确定两条 3D 线段是否相交?如果它们确实相交,在什么 XYZ 位置? 我只能找到 2D 的答案:How do you detect where two l
给定每条线的起点和终点的 XYZ 坐标,如何确定两条 3D 线段是否相交?如果它们确实相交,在什么 XYZ 位置? 我只能找到 2D 的答案:How do you detect where two l
我正在使用适用于 ios 的 google map sdk 来提供当前用户位置和结束位置之间的方向。到目前为止,我已经使用下面的代码在当前用户位置和结束位置之间绘制了一条 GMSPolyline,并且
我是 Qt 的新手,我想使用 Qt 使用 CGAL 制作交互式几何程序。我希望用户使用鼠标输入点、线段,然后按下按钮让 CGAL 算法处理输入。 我的环境是 CGAL 4.5、Qt 5.6 和 QtC
我有两条线段:X1,Y1,Z1 - X2,Y2,Z2 和 X3,Y3,Z3 - X4,Y4,Z4 我试图找到两个线段之间的最短距离。 几个小时以来,我一直在寻找解决方案,但所有这些解决方案似乎都适用于
我正在尝试在 WPF 中创建铁路轨道和带有边界和标签的街道等效果。如何向线段添加边框和沿线段的标签?我试过 Border 类,但它创建了一个矩形边框。 对于标签,我尝试了 Text on a path
我正在做一个小项目来显示基于路线段重叠的路线效率低下。 例如,我在这里放了一个 JSFIDDLE,显示 D 和 E 之间有一条粉红色和蓝色的线重叠。我如何确定这段路在它们的路线上有重叠? 路线将由用户
我想绘制三组数据。具体来说,我想显示单个数据点,包括三组的均值。这是我到目前为止所拥有的: library(ggplot2) df <- data.frame(group=rep(c("A", "B"
我想绘制三组数据。具体来说,我想显示单个数据点,包括三组的均值。这是我到目前为止所拥有的: library(ggplot2) df <- data.frame(group=rep(c("A", "B"
<line> 元素可以用来画线段 SVG 线段 <line> <line> 元素可以用来画线段 线段的起始坐标可以用 x1 和 y1 来定义 线段的终点坐标可
我正在我的游戏中编写 C++ 碰撞检测程序,并试图提出一种算法:我有一个由两个中心点(C1、C2)、长度和半径定义的胶囊。然后我有一条用两点(R1,R2)定义的射线。我已经知道它们相交了。我只需要找到
我正在创建一个包含多变量数据的 PCA 双图。 有没有办法在 ggbiplot 中指定线段的颜色/透明度/位置?此命令的所有参数均未提供此选项。 我知道 ggbiplot 是基于 ggplot - 它
最近学了下 python opencv,分享下使用 opencv 在图片上绘制常用图形的方法。 案例中实现了在图片中添加线段、圆形、矩形、椭圆形以及添加文字的方法,使用 opencv2 实现的
我在应用 rgl 3d 绘图包时遇到了一些问题。 我正在尝试绘制一些线段。我的数据被安排在一个名为“标记”的数据框中,它有六列,一列代表起始 x、y 和 z 值,一列代表结束 x、y 和 z 值。 s
我必须使用 matplotlib 库绘制多条“曲线”,每条曲线由水平线段(甚至点)组成。 我通过 NaNs 分隔片段达到了这个目标。这是我的示例(工作)代码: from pylab import ar
我是一名优秀的程序员,十分优秀!