考虑所有contiguos子阵列

时间:2015-08-10 22:14:02

标签: arrays algorithm sub-array

考虑一个数组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)

我已经尝试了很多以上的算法,但无法提高时间复杂度。我怎么能这样做?任何提示,方法都将受到赞赏。

3 个答案:

答案 0 :(得分:3)

根据Paul的回答,我使用简化版本的分段树实现了O(n log n)版本。您会注意到此答案与Paul的答案相同,但优化版本为leftctsmallerrightctsmaller

在实践中,它的作用是采用数组,比方说:

A = [5,1,7,2,3,7,3,1]

构造一个支持数组的树,如下所示:

segment tree

在树中,第一个数字是值,第二个数字是数组中出现的索引。每个节点是其两个子节点中的最大节点。此树由数组(非常类似于堆树)支持,其中索引i的子项位于索引i*2+1i*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)