有一个简单的迭代动态规划算法来解决这个问题:对于从 1 到n
(排列长度)的所有 i,取数字并查看左侧有i
多少元素已经被看到。由于我们按递增顺序处理,我们知道未看到的元素是大于- 的元素,因此我们计算并记下这些元素的数量。诀窍是引入一个外部列表,而不是跟踪已经看到的元素。P
i
i
i
P
首先,让我们看看如何以方式做到这O(n^2)
一点。例如,如果P=[4, 3, 2, 1]
,那么算法将按如下方式执行:
创建一个tree
初始化为零的结构。如果迭代算法已经看到j
第 j 个位置的元素,则它的位置保持“1” 。P
取 1,确定pos==3
。在 中写下“1” tree[pos]
。计算num_seen=sum(tree[0:3])
等于 0。写pos - num_seen + 1
在 处I[0]
。在这之后:tree = [0, 0, 0, 1], I = [3, 0, 0, 0]
取 2,在树 [1] 中写下“1”,在 I[1] 处写下 1。tree = [0, 1, 0, 1], I=[3,1,0,0]
.
取 3,在树[2] 中写下“1”,在 I[2] 中写下 0。tree = [0, 1, 1, 1], I=[3,1,0,0]
.
取 4,在树 [0] 中写下“1”,在 I[3] 处写下 0。tree = [1, 1, 1, 1], I=[3,1,0,0]
.
第二个技巧是使用有效的数据结构来及时计算所见元素的数量,O(n log n)
而不是O(n^2)
像上面那样。
这是使用 Fenwick 树快速计算可见元素数量的 Python 代码:
def ft_sum(tree, a, b):
if a == 0:
s = 0;
while b >= 0:
s += tree[b];
b = (b & (b + 1)) - 1
return s
return ft_sum(tree, 0, b) - ft_sum(tree, 0, a - 1)
def ft_adjust(tree, k, v):
while k < len(tree):
tree[k] += v
k |= k + 1
def calcI(P):
n = len(P)
tree = [0] * n
I = [0] * n
positions = [0] * n
for i in xrange(n):
positions[P[i]-1] = i
tree = [0] * n
for i in xrange(n):
pos = positions[i]
ft_adjust(tree, pos, 1)
num_seen = ft_sum(tree, 0, pos)
I[i] = pos - num_seen + 1
return I