您的位置:首页 > 其它

Segment Tree介绍与使用

2013-07-25 11:13 405 查看
首先,看如下事例:

假设有数组 A[0..n-1],我们可以进行如下操作:

(1)求范围[ l, r ]间元素之和。0<=l<=r<=n-1;记为 Query 操作;

(2)更新某位置 i 处值,A[i] = new_val, 0<=i<=n-1;记为 Update 操作;

简单直观方法,就是遍历数组,可知此时(1)时间复杂度O(n),(2)时间复杂度O(1)。这种方法对于多更新操作的有利。

另一方法,创建新数组B,其中B[i]=SUM(A[0]..A[i]),0<=i<=n-1。此时(1)时间复杂度O(1),(2)时间复杂度O(n)。这种方法对于多查询操作的有利。

那么当两类操作差不多的情况下。有没有好的方法的呢?这里就引出了Segment Tree 的数据结构,通过使用它,我们可以将两类操作的时间复杂度都控制在O(lgn)。

Segment Tree 的表示描述如下:

1.叶节点中存储了数组中元素。

2.中间节点中存储了表述部分叶节点 Merge 后节点,不同使用场景下,Merge含义不同。上例中,Merge的含义就是叶节点求和。

Segment Tree 数据结构的实现,通常以数组方式来组织表示,对于下标为 i 的节点,其左孩子下标为 2*i +1,右孩子下标为 2*i +2。父节点的下标为



例子:数组 { 1,3, 5, 7, 9, 11 },更新位置值和查询任意区间和。建立 Segment Tree 如下,



如何针对给定数组建立 Segment Tree?

对于数组段 arr[0...n-1],每次我们将当前段分为二等分。递归地进行下去,直到当前段长度为1,处理每段时我们在相应节点存储和值。

建立Segment Tree每层(除了叶节点层)都是满的,也就是该树可以看着是完全二叉树(完全二叉树 + n个叶节点),所以Segment

Tree总节点数目为 2*n + 1。(n个叶节点+ n-1个中间节点)。树的高度为

。由于以数组来表示树结构,需要开辟的数组的大小为




查询给定范围的和?

树建立后,可以使用如下算法来获取范围和。

int getSum( node, l, r)

{

if range of node is within l and r

return value in node

else if range of node is completely outside l and r

return 0

else

return getSum(node's left child, l ,r ) + getSum(node's right child, l , r)

}

如何更新值?

同建树算法,和查询算法一样,递归的更新。对于给定要更新值的下标 i,令 diff = new_val - old_val。从根节点开始,对于所有 i 在节点的 range of node。

node值加diff。不在节点代表range中, 不进行更新。

void upDate(node , i, diff)

{

if i within range of node

node.value = node.value + diff;

upDate( node's left child, i, diff );

upDate( node's right child, i, diff );

}

实现代码如下:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: