考虑一个数组A = [5,1,7,2,3]
所有contiguos子阵列= {[5],[1],[7],[2],[3],[5,1],[1,7],[7,2],[2,3] ] [5,1,7],[1,7,2],[7,2,3],[5,1,7,2],[1,7,2,3],[5,1,7 ,2,3]}
用上面的最大元素替换上面集合中的所有数组:
set将如下所示:{[5],[1],[7],[2],[3],[5],[7],[7],[3], [7],[7],[7],[7],[7],[7]}
频率信息:[5] - > 2,[1] - > 1,[7] - > 9,[2] - > 1,[3] - > 2
我的目标是找到上述频率信息。
我的方法:
首先列出对(x,y)的列表。 x是A中的元素,其索引是y。
列表:[(5,1),(1,2),(7,3),(2,4),(3,5)]
按照相对于第一个元素的递减顺序对列表进行排序。现在,
列表:[(7,3),(5,1),(3,5),(2,4),(1,2)]
算法:
def f( array, first_index, last_index):
->select an element from LIST starting from left which
is not marked as visited and (first_index <= element.second <=
last_index)
->calculate frequency info of element in tuple as (element.secondvalue-first_index+1) + (element.secondvalue-first_index+1)*(last_index - element.second_value)
->Mark above element as visited
->Recursively solve f( array, first_index,element.secondvalue-1 ),f( array,element.secondvalue+1,last_index)
我们可以轻松设置合适的基础案例。
时间复杂度:O(n * n)
我已经尝试了很多以上的算法,但无法提高时间复杂度。我怎么能这样做?任何提示,方法都将受到赞赏。
答案 0 :(得分:3)
根据Paul的回答,我使用简化版本的分段树实现了O(n log n)版本。您会注意到此答案与Paul的答案相同,但优化版本为leftctsmaller
和rightctsmaller
。
在实践中,它的作用是采用数组,比方说:
A = [5,1,7,2,3,7,3,1]
构造一个支持数组的树,如下所示:
在树中,第一个数字是值,第二个数字是数组中出现的索引。每个节点是其两个子节点中的最大节点。此树由数组(非常类似于堆树)支持,其中索引i
的子项位于索引i*2+1
和i*2+2
中。
然后,对于每个元素,很容易找到最近的更大元素(在它之前和之后)。
例如,为了找到左边最近的更大元素,我们在树中搜索第一个父级,其中值大于且索引小于参数。答案必须是这个父母的孩子,然后我们在树中寻找满足相同条件的最右边的节点。
我已经用Python实现了它(以及天真的版本,检查答案),它似乎运行良好。
import sys, random
from collections import defaultdict
from math import log, ceil
def make_tree(A):
n = 2**(int(ceil(log(len(A), 2))))
T = [(None, None)]*(2*n-1)
for i, x in enumerate(A):
T[n-1+i] = (x, i)
for i in reversed(xrange(n-1)):
T[i] = max(T[i*2+1], T[i*2+2])
return T
def print_tree(T):
print 'digraph {'
for i, x in enumerate(T):
print ' ' + str(i) + '[label="' + str(x) + '"]'
if i*2+2 < len(T):
print ' ' + str(i)+ '->'+ str(i*2+1)
print ' ' + str(i)+ '->'+ str(i*2+2)
print '}'
def find_generic(T, i, fallback, check, first, second):
j = len(T)/2+i
original = T[j]
j = (j-1)/2
#go up in the tree searching for a value that satisfies check
while j > 0 and not check(T[second(j)], original):
j = (j-1)/2
#go down in the tree searching for the left/rightmost node that satisfies check
while j*2+1<len(T):
if check(T[first(j)], original):
j = first(j)
elif check(T[second(j)], original):
j = second(j)
else:
return fallback
return j-len(T)/2
def find_left(T, i, fallback):
return find_generic(T, i, fallback,
lambda a, b: a[0]>b[0] and a[1]<b[1], #value greater, index before
lambda j: j*2+2, #rightmost first
lambda j: j*2+1 #leftmost second
)
def find_right(T, i, fallback):
return find_generic(T, i, fallback,
lambda a, b: a[0]>=b[0] and a[1]>b[1], #value greater or equal, index after
lambda j: j*2+1, #leftmost first
lambda j: j*2+2 #rightmost second
)
def optimized_version(A):
T = make_tree(A)
answer = defaultdict(lambda: 0)
for i, x in enumerate(A):
left = find_left(T, i, -1)
right = find_right(T, i, len(A))
answer[x] += (i-left) * (right-i)
return dict(answer)
def naive_version(A):
answer = defaultdict(lambda: 0)
for i, x in enumerate(A):
left = next((j for j in range(i-1, -1, -1) if A[j]>A[i]), -1)
right = next((j for j in range(i+1, len(A)) if A[j]>=A[i]), len(A))
answer[x] += (i-left) * (right-i)
return dict(answer)
A = [random.choice(xrange(32)) for i in xrange(8)]
MA1 = naive_version(A)
MA2 = optimized_version(A)
sys.stderr.write('Array: ' + str(A) + '\n')
sys.stderr.write('Naive: ' + str(MA1) + '\n')
sys.stderr.write('Optimized: ' + str(MA2) + '\n')
sys.stderr.write('OK: ' + str(MA1==MA2) + '\n')
#print_tree(make_tree(A))
答案 1 :(得分:1)
我怀疑您的代码是否在O(n^2)
中运行。无论如何,以更有效的方式解决这个问题的一种方法是将每个数字映射到左/右项目的数量,这些项目小于给定项目。例如:
input = [2 , 3 , 1 , 5 , 4 , 8 , 0]
for number n = 5
leftctsmaller(n) = 3
rightctsmaller(n) = 1
此地图需要生成O(n^2)
。其余的很简单。给定左侧和右侧的空间,我们可以轻松确定仅包含小于n
的数字的子阵列数,但n
本身除外。
答案 2 :(得分:1)
从下到上遍历您的值到索引的映射 - 维护一个增强的间隔树。每次添加索引时,请调整适当的间隔并计算相关段的总计:
A = [5,1,7,2,3] => {1:1, 2:3, 3:4, 5:0, 7:2}
indexes interval total sub-arrays with maximum exactly
1 (1,1) 1 => 1
1,3 (3,3) 2 => 1
1,3,4 (3,4) 3 => 2
1,3,4,0 (0,1) 5 => 2
1,3,4,0,2 (0,4) 7 => 3 + 2*3 = 9
augmented trees中的插入和删除时间复杂度为O(log n)
。最坏情况的总时间复杂度为O(n log n)
。