[线段树]深入理解:线段树的构建和分解方法
2015-07-16 08:28
309 查看
如果还不了解基本的线段树,请点击这里查看。
——线段树的构造,实际上是利用了二分的方法。每次在构造相应区间时,需要按照二分的规则来继续分解,并构造区间内的子区间,存成一个新的节点,并以此保留新的信息。构造整棵线段树时,需要把每个节点分解,直到所有的节点长度缩减为1时才可以停止。
——线段树的分解,遵循的规则和线段树的构造是类似的。不同之处在于:分解的规则就是:如果有某个节点的区间完全属于待分解区间,则该节点为终止节点,不再继续往下分解。所有终止的节点所代表的区间都相互不重叠,且加在一起就恰好等于整个待分解的区间。
线段树的构建:
function 以节点v为根建树、v对应的区间为L,R
{
对节点进行初始化,插入相应的节点基本信息
if(L != R)
{
以v的左孩子为根建树,区间为L,(L+R)/2
以v的右孩子为根建树,区间为(L+R)/2+1,R
}
}
因此,建树的时间复杂度为O(n),n是根节点对应的区间长度。
线段树的更新:
function 以节点为v,插入第i个位置的,其值为val
{
if(插入位置在线段树的区间内)
更新相应的节点
if (插入位置在线段树区间的左半边)
{ 以v的左孩子为根继续更新节点,其位置为i,节点为val }
else if (插入位置在线段树区间的右半边)
{ 以v的右孩子为根继续更新节点,其位置为i,节点为val
}
}
线段树的查询:
function 以根节点为v,查询的左节点为L,右节点为R
{
if (查询的节点就是当前树的左右节点)
返回查询信息,结束查询
else
{
取mid = (L+R)/2
查询的区间与 [L,mid]或[mid+1,R]哪个有交集,就进入哪个区间进行进一步查询。
}
}
用线段树解题,关键是要想清楚每个节点要存哪些信息(当然区间的起点终点,以及左右子节点的指针是必须的),以及这些信息如何高效更新,维护,查询。不要一更新就更新到叶子节点,那样更新效率最坏就可能变成O(n)的了。
——线段树的构造,实际上是利用了二分的方法。每次在构造相应区间时,需要按照二分的规则来继续分解,并构造区间内的子区间,存成一个新的节点,并以此保留新的信息。构造整棵线段树时,需要把每个节点分解,直到所有的节点长度缩减为1时才可以停止。
——线段树的分解,遵循的规则和线段树的构造是类似的。不同之处在于:分解的规则就是:如果有某个节点的区间完全属于待分解区间,则该节点为终止节点,不再继续往下分解。所有终止的节点所代表的区间都相互不重叠,且加在一起就恰好等于整个待分解的区间。
线段树的构建:
function 以节点v为根建树、v对应的区间为L,R
{
对节点进行初始化,插入相应的节点基本信息
if(L != R)
{
以v的左孩子为根建树,区间为L,(L+R)/2
以v的右孩子为根建树,区间为(L+R)/2+1,R
}
}
因此,建树的时间复杂度为O(n),n是根节点对应的区间长度。
线段树的更新:
function 以节点为v,插入第i个位置的,其值为val
{
if(插入位置在线段树的区间内)
更新相应的节点
if (插入位置在线段树区间的左半边)
{ 以v的左孩子为根继续更新节点,其位置为i,节点为val }
else if (插入位置在线段树区间的右半边)
{ 以v的右孩子为根继续更新节点,其位置为i,节点为val
}
}
线段树的查询:
function 以根节点为v,查询的左节点为L,右节点为R
{
if (查询的节点就是当前树的左右节点)
返回查询信息,结束查询
else
{
取mid = (L+R)/2
查询的区间与 [L,mid]或[mid+1,R]哪个有交集,就进入哪个区间进行进一步查询。
}
}
用线段树解题,关键是要想清楚每个节点要存哪些信息(当然区间的起点终点,以及左右子节点的指针是必须的),以及这些信息如何高效更新,维护,查询。不要一更新就更新到叶子节点,那样更新效率最坏就可能变成O(n)的了。
const int MAXN = 200050; const int INF = 0x3f3f3f3f; struct node { int L,R; int sum; }; node tree[MAXN * 3]; void build(int root,int L,int R) { tree[root].L = L; tree[root].R = R; tree[root].sum = 0; if (L != R) { build(root * 2 +1,L,(L+R)/2); build(root * 2 +2,(L+R)/2+1,R); } } void update(int root,int i,int val) // 第i个数字,其值为val,插入. { if (i>=tree[root].L && i<=tree[root].R) tree[root].sum += val; if (i <= (tree[root].L + tree[root].R)/2 ) { update(2*root+1,i,val); } else { update(2*root+2,i,val); } } int sum; void querySum(int root,int l,int r) { cout<<root<<" "<<l<<" "<<r<<endl; if (tree[root].L == l && tree[root].R == r) { sum += tree[root].sum; return;} if (r <=(tree[root].L+tree[root].R)/2) querySum(root*2+1,l,r); else if( l > (tree[root].L+tree[root].R)/2) querySum(root*2+2,l,r); else { querySum(root*2+1,l,(tree[root].L+tree[root].R)/2); querySum(root*2+2,(tree[root].L+tree[root].R)/2+1,r); } }
相关文章推荐
- 基于java的http服务器
- [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能
- scala数组
- Linux文件系统的实现
- JS 事件派发器EventDispatcher
- 【原创】 SharePoint Service Unavaliable
- Justinmind教程(3)——管理原型
- 函数作用
- 启动tomcat时startup.bar、catalina.bat等窗口一闪而过的解决方法
- 自己的自定义单元格(IOS)
- I - Fire Game
- 鹊桥仙·咏牵牛花
- 国军抗战全纪实
- Dairy20150716
- meta 数据解析
- 8.String to Integer (atoi) (INT; Overflow)
- 压缩解压总结
- tomcat启动时报webapps文件夹下的文件夹部署目录不正确的错误
- 缓存池扩展 (Buffer Pool Extension)实践
- Meta 元数据之百度开发适配器