您的位置:首页 > 其它

[线段树]深入理解:线段树的构建和分解方法

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)的了。

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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: