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 );
}
实现代码如下:
假设有数组 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 );
}
实现代码如下:
相关文章推荐
- Swing文档大全 - Swing中树(Tree)的使用与介绍
- jQuery LigerUI 插件介绍及使用之ligerTree
- 介绍现在项目中使用到的dojo tree
- 一个基于jQuery的树型插件(OrangeTree)使用介绍
- Fancytree Javascript Tree TreeTable 树介绍和使用
- jQuery LigerUI 插件介绍及使用之ligerTree
- jQuery LigerUI 插件介绍及使用之ligerTree
- Java 集合系列之 Set架构 TreeSet HashSet 详细介绍(源码解析)和使用示例
- Aga.Controls.Tree.TreeViewAdv使用教程之概要介绍
- 一个基于jQuery的树型插件(OrangeTree)使用介绍
- jQuery LigerUI 插件介绍及使用之ligerTree
- linux系统中scp命令的使用介绍
- Detour开发包介绍(2):使用
- SQL Server系统表sysobjects介绍与使用
- [EntLib]微软企业库5.0 学习之路——第五步、介绍EntLib.Validation模块信息、验证器的实现层级及内置的各种验证器的使用方法——上篇
- JavaScript中使用Object.create()创建对象介绍
- KVM虚拟化(一)—— 介绍与简单使用
- Android MeasureSpec介绍及使用
- Java 语言中 Enum 类型的使用介绍
- svn的简单介绍及其在windows系统下的安装及使用