Arrays 用于在优于O(logn)的预期时间内查找子阵列最大值的数据结构
给定一个值数组,如何构建一个数据结构,让您快速找到任何连续子数组的最大值?理想情况下,建造这种结构的开销应该很小,结构应该允许有效的附加和单个元素的变异Arrays 用于在优于O(logn)的预期时间内查找子阵列最大值的数据结构,arrays,data-structures,language-agnostic,time-complexity,Arrays,Data Structures,Language Agnostic,Time Complexity,给定一个值数组,如何构建一个数据结构,让您快速找到任何连续子数组的最大值?理想情况下,建造这种结构的开销应该很小,结构应该允许有效的附加和单个元素的变异 示例数组是[6,2,3,7,4,5,1,0,3]。请求可能是从索引2到7(子数组[3,7,5,1,0])查找切片的最大值,这将导致7让n为数组的长度,k为切片的长度 天真的,O(logk),方法 一个显而易见的解决方案是构建一棵树,反复给出最大值的成对摘要 1 8 4 5 4 0 1 5 6 9 1 7 0 4 0 9 0 7 0 4 5 7
示例数组是
[6,2,3,7,4,5,1,0,3]
。请求可能是从索引2到7(子数组[3,7,5,1,0]
)查找切片的最大值,这将导致7
让n
为数组的长度,k
为切片的长度
天真的,O(logk)
,方法
一个显而易见的解决方案是构建一棵树,反复给出最大值的成对摘要
1 8 4 5 4 0 1 5 6 9 1 7 0 4 0 9 0 7 0 4 5 7 4 3 4 6 3 8 2 4 · ·
8 5 4 5 9 7 4 9 7 4 7 4 6 8 4 ·
8 5 9 9 7 7 8 4
8 9 7 8
9 8
9
这些摘要最多占用O(n)
空间,使用短索引可以有效地存储较低级别。例如,底层可以是位数组。附加和单个突变需要O(logn)
时间。如果需要,还有许多其他领域需要优化
所选切片可以拆分为两个切片,在两个三角形之间的边界上拆分。在本例中,对于给定的切片,我们将拆分为:
|---------------------------------|
6 9 1 7 0 4 0 9|0 7 0 4 5 7 4 3 4 6 3 8 2 4 · ·
9 7 4 9 | 7 4 7 4 6 8 4 ·
9 9 | 7 7 8 4
9 | 7 8
| 8
在每个三角形中,我们感兴趣的是这些树中的一部分,它们最小限度地决定了我们真正关心的元素:
|---------------------------------|
1 7 0 4 0 9|0 7 0 4 5 7 4 3 4 6 3
7 4 9 | 7 4 7 4 6
9 | 7 7
| 7
请注意,在这种情况下,左侧有两棵树,右侧有三棵树。树的总数最多为O(log k)
,因为任何给定高度中最多有两棵树。我们可以通过一点数学来找到分裂点
round_to = (start ^ end).bit_length() - 1
split_point = (end >> height) << height
很难遍历整数的最高有效位,但如果您进行位交换(一条byteswap指令加上几个移位和掩码),则可以通过迭代来遍历最低有效位:
new_value = value & (value - 1)
lowest_set_bit = value ^ new_value
value = new_value
沿左半部和右半部向下遍历需要O(logk)
预期时间,因为最多有2log₂ k
树-每边一位
切线:在O(1)
时间和O(n logn)
空间中处理残差
O(logk)
比O(logn)
好,但它仍然不是突破性的。上一次尝试的一个有益效果是,两边的树都“附着”在一边;在它们的切片中只有n
范围,而不是任意切片的n²
。您可以通过向每个级别添加累积最大值来利用这一点,如下所示:
1 8 4 5 4 0 1 5 6 9 1 7 0 4 0 9 0 7 0 4 5 7 4 3 4 6 3 8 2 4 · ·
- 8|- 5|- 4|- 5|- 9|- 7|- 4|- 9|- 7|- 4|- 7|- 4|- 6|- 8|- 4|- · left to right
8 -|5 -|4 -|5 -|9 -|7 -|4 -|9 -|7 -|4 -|7 -|4 -|6 -|8 -|4 -|· - right to left
- - 8 8|- - 4 5|- - 9 9|- - 4 9|- - 7 7|- - 7 7|- - 6 8|- - · · left to right
8 8 - -|5 5 - -|9 9 - -|9 9 - -|7 7 - -|7 7 - -|8 8 - -|4 4 - - right to left
- - - - 8 8 8 8|- - - - 9 9 9 9|- - - - 7 7 7 7|- - - - 8 8 · · left to right
8 8 5 5 - - - -|9 9 9 9 - - - -|7 7 7 7 - - - -|8 8 8 8 - - - - right to left
- - - - - - - - 8 9 9 9 9 9 9 9|- - - - - - - - 7 7 7 8 8 8 · · left to right
9 9 9 9 9 9 9 9 - - - - - - - -|8 8 8 8 8 8 8 8 - - - - - - - - right to left
- - - - - - - - - - - - - - - - 9 9 9 9 9 9 9 9 9 9 9 9 9 9 · · left to right
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 - - - - - - - - - - - - - - - - right to left
标记-
用于忽略那些不需要复制的部分,这些部分必须与它们下面的级别相同。在这种情况下,相关的切片是
|---------------------------------|
1 7 0 4 0 9 0 7 0 4 5 7 4 3 4 6 3
↓ ↓
9 9 9 9 - - - -|- - - - - - - - 7 7 7 8 8 8 · ·
right to left | left to right
所需的最大值如图所示。真正的最大值就是这两个值的最大值
这显然需要O(n logn)
内存,因为有logn
级别,每个级别都需要一行完整的值(尽管它们可以作为索引以节省空间)。但是,更新需要花费O(n)
时间,因为它们可能会传播-例如,向该行添加10将使整个从右下到左的行无效。突变显然同样低效
O(1)
通过回答不同的问题来计时
根据所需的上下文,您可能会发现可以截断搜索深度。如果您的切片相对于切片的大小有一定的回旋余地,那么这是可行的。由于切片在几何上收缩,尽管0:4294967295
的切片需要大量的22次迭代,但截断到11次迭代的固定数量会使切片0:4292870144
的最大值相差0.05%。这是可以接受的
O(1)
利用概率的预期时间
舍入可能是可以接受的,但即使是这样,您仍在执行O(logn)
算法-只需使用较小的、固定的n
。在随机分布的数据上可以做得更好
想想森林的一边。当你向下移动时,你看到的数字的分数超过了你几何上没有看到的分数。因此,你已经看到最大的概率在PAR中增加。这是有道理的,你可以利用这对你的优势
再考虑一下这一半:
---------------------|
0 7 0 4 5 7 4 3 4 6 3 8 2 4 · ·
7 4 7 4 6* 8 4 ·
7 7 8* 4
7* 8
8
检查7*
后,不要立即遍历6*
。取而代之的是,检查所有其余项的最小父项,即8*
。仅当此父项大于到目前为止的最大值时才向下遍历。如果不是,则可以停止迭代。只有当它更大时,您才需要继续向下移动。碰巧最大的值在这里超过了端点,所以我们一直向下遍历,但你可以想象这是不寻常的
至少有一半的时间你只需要计算第一个三角形,剩下的至少有一半时间你只需要再往下看一次,等等。这是一个几何序列,显示平均遍历成本是两次遍历;如果包含以下事实,剩余三角形在某些情况下可能小于一半大小
在最坏的情况下呢?
最坏的情况发生在非随机树上。最具病理性的是分类数据:
---------------------|
0 1 2 3 4 5 6 7 8 9 a b c d e f
1 3 5 7 9 b d f
3 7 b f
7 f
f
因为最大值总是在你没有看到的范围的片段中,不管你选择哪个片段。因此,遍历总是O(logn)
。不幸的是,排序数据在实践中很常见,并且该算法在这里受到了损害(该属性与其他一些算法共享,如quicksort)。不过,减轻伤害是可能的
不因排序数据而死亡
如果每个节点都说它是排序的,还是反向排序的,那么在到达该节点时,您不需要再进行任何遍历-您只需获取子数组中的第一个或最后一个元素
---------------------|
0 1 2 3 4 5 6 7 8 9 a b c d e f
→ → → → → → → →
→ → → →
→ →
→
不过,您可能会发现,大部分数据都是通过一些小的随机化进行排序的,这打破了计划:
---------------------|
0 1 2 3 4 5 6 7 a 9 a b d 0 e f
→ → → → ← → ← →
→ → b f
→ f
f
因此,每个节点可以有t
---------------------|
0 1 2 3 4 5 6 7 a 9 a b d 0 e f
→ → → → ← → ← →
→ → b f
→ f
f
---------------------|
0 1 2 3 4 5 6 7 a 9 a b d 0 e f
→1 →1 →1 →1 ←1 →1 ←1 →1
0 3 5 7 a b d f
→2 →2 →1 →1
3 7 b f
→3 →2
7 f
→3
f