用于查找几乎排序的间隔数的高效算法

时间:2014-07-31 11:14:04

标签: python algorithm

在一个问题中,我必须找到满足以下条件的数组的连续子序列的数量(它们被称为几乎排序的间隔)

  1. 序列中的第一个数字是最小的。
  2. 最后一个数字是最大的。
  3. 我写了下面的代码,它给了我超出时间限制的错误。如何优化算法和代码。

    import sys
    
    
    def ans(values):  
    
    total=0;
    small=[]    # Taking a array which contains the smallest till that index
    svar1,lvar2=values[0],values[0]
    
    for i in range(len(values)):
        if svar1<values[i]:
            small.append(svar1)
        elif svar1>=values[i]:   
            svar1=values[i]
            small.append(svar1)
    
    for i in range(len(values)):   # for each value
        k=i
        highest=values[i]          # we consider the highest 
        lowest=values[i]           # we consider the lowest
        flag=0
        while k>=0:                # iterating back
            if values[k]>highest: #if we encounter an element greater than the last element 
                break
            if lowest>=values[k]:  #I try to maintain the lowest if I encounter one.
                total+=1             # counting for the answer
                lowest=values[k]     
            if values[k]==small[k]:  #I use that array and try to break the loop if I found that the smallest till then and the lowest are same.
                flag=1
            if k>0 and flag==1:
                if values[k-1]>small[k]:
                    break
    
            k-=1
    return total
    
    if __name__=="__main__":
        y=input()
        values=map(int,sys.stdin.readline().split(" "))
        print ans(values)
    

    抱歉粘贴整个代码。我希望这是可以理解的。我可以提一下如何优化它吗?数组长度为1<=N<=10^6

    示例:

    3 1 2 5 4 will give answer as 8
    

2 个答案:

答案 0 :(得分:0)

获取第一个元素ele[0]minimum且最后一个元素ele[-1]max

的所有子序列
values = [3,1,2,5,5]

def conseq_sequences(li):
    seq = []
    for i in range(len(li) + 1):
        seq += ([ele for ele in zip(*(li[i:] for i in range(i))) if ele[0] == min(ele) and ele[-1] == max(ele)])
    return len(filter(None, seq)) # remove []

In [5]:  conseq_sequences(values)
Out[5]: 11

如果我们的值为[1,3,1,2,5,5],我们会使用+ 1来包含完整列表。

for i in range(len(li) + 1):

然后我们遍历每个可能的子序列,过滤掉与我们的条件不匹配的元素。

seq += ([ele for ele in zip(*(li[i:] for i in range(i))) if ele[0] == min(ele) and ele[-1] == max(ele)])

最后,我们返回列表的len,过滤任何空列表[]:

(filter(None, seq)) = [(3,), (1,), (2,), (5,), (5,), (1, 2), (2, 5), (5, 5), (1, 2, 5), (2, 5, 5), (1, 2, 5, 5)]

答案 1 :(得分:0)

网站HackerRank.com给出的一个问题是计算N个数字排列的几乎排序的间隔数,其值范围从1到N.

数组的间隔定义为任何连续的非空数字子集。例如,如果数组定义为{3,4,1,5,2},则有效间隔将包括{5},{1,5},{3,4,1}。

阵列的几乎整理的间隔是如上所述的任何间隔加上要求第一个数字小于或等于间隔中的所有其他数字,并且最后一个数字大于或等于所有其他数字。< / p>

使用上面的数组,几乎排序的间隔集将包括{3,4},{1,5},{5}但不包括{5,2}。

因此整个几乎排序的间隔集合将是{{3},{3,4},{4},{1},{1,5},{5},{2}}。因此,几乎排序的间隔的数量是7。

为了迎接挑战,您的解决方案必须在O(n * log n)时间内解决问题。

O(n * n)解决方案相当简单。 O(n * log n)需要更多努力。

我发现问题非常困难,因为我原来的O(n * log n)非常混乱,觉得有更好的方法。搜索网络真的没什么用,除了一些人提供一些真正没有多大帮助的可怕提示。当我最终浏览HackerRank的“编辑”部分时,对更优雅的解决方案的描述很难理解。经过一番努力,我终于明白了解决方案是如何运作的。

定义两个数组以帮助解决在数组中找到所有几乎已排序的间隔的问题:left [i] = j其中j&lt; i和j最接近i和a [j]&gt; a [i] right [i] = j其中j> i和j最接近i和a [j]&lt; A [1]

这些数组有助于定义两个索引i和j何时构成几乎排序的间隔。对于一些i和j,如果j <1,则[i] ... a [j]是几乎排序的间隔。对[i]和我&lt;左[J]。

对于数组a [] = {3,4,1,5,2},左[] = {-1,-1,2,-1,3}和右[] = {2,2, 5,4,5}。请注意,我们使用-1表示左数组表示向左的界限位置,使用值5表示(即N表示)向右表示越界位置。

观察我们的阵列,我们看到3&lt;右[2]和2&gt; left [3]表示区间{a [2],a [3]}(即{1,5})是一个几乎排序的区间。

有一点值得注意的是,一旦我们定义了left []和right [],我们就已经“编码”了数字并且彼此之间存在关系,这使我们现在能够解决问题,只处理数组的索引和不再是构成阵列的数字。

左[]和右[]数组都可以在O(n)时间内计算。

然后我们可以使用这些数组来有效地计算几乎排序的间隔的总数。我们跨越指数从右到左迭代。当我们遍历数组时,我们可以保留一个集B,它包含从当前索引的左边开始的所有间隔的所有可能的结束索引。

这可以通过在索引i处将索引值i添加到集合B并且在索引left [i]处移除索引值i(其将始终是i的左侧的一些索引)来完成。维持集合B可以在O(1)时间内完成。

对于每个索引,如果当前索引是起始索引,我们可以检查B集中有多少索引是有效的结束索引。

在索引i处,索引j仅在i> 1时才在B集中。左[J]。如果j <1,则区间{a [i] ... a [j]}是几乎排序的区间。右[i]中。我们可以计算集合B中的多少指数小于正确[i]以知道有多少几乎排序的间隔索引位置i对几乎排序的间隔的总数(作为间隔的左索引位置)的贡献。如果我们然后在所有索引中累积这些值,我们可以找到几乎排序的间隔的总数。

这只留下我们如何计算B中的索引数量,这些索引的数量小于正确[i]。这可以使用二进制索引树在O(log n)时间内完成。

因此总运行时间为O(n * log n)。