Python 求几乎排序区间数的有效算法

Python 求几乎排序区间数的有效算法,python,algorithm,Python,Algorithm,在一个问题中,我必须找到一个数组的连续子序列的数量,这些子序列满足以下条件,它们被称为几乎排序区间 序列中的第一个数字是最小的。 最后一个数字是最大的。 我写了下面的代码,这是给我的时间限制超过错误。如何优化算法和代码 import sys def ans(values): total=0; small=[] # Taking a array which contains the smallest till that index svar1,lvar2=values[0],val

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

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

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)

获取第一个元素ele[0]为最小值,最后一个元素ele[-1]为最大值的所有子序列

如果我们有[1,3,1,2,5,5]这样的值,我们使用+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)]

HackerRank.com网站上给出的一个问题是,在N个值范围为1到N的数字的排列中,计算几乎排序的区间数

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

数组的几乎排序间隔是如上所述的任何间隔加上第一个数字小于或等于间隔中所有其他数字,最后一个数字大于或等于所有其他数字的要求

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

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

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

On*n解决方案相当简单。登录n需要更多的努力

我发现这个问题很难解决,因为我的原始日志非常混乱,我觉得有更好的方法。搜索网页真的没有什么帮助,除了一些人给出一些可怕的提示,真的没有什么帮助。当我最后看了一眼HackerRank的编辑部分时,一个更优雅的解决方案的描述很难理解。经过一番努力,我终于明白了解决方案是如何工作的

定义两个数组以帮助解决查找数组中所有几乎已排序的区间的问题:左[i]=j,其中ja[i]右[i]=j,其中j>i,j最接近i,a[j] 这些数组有助于定义两个索引i和j何时构成几乎排序的间隔。对于一些i和j,a[i]。。。如果j 对于数组a[]={3,4,1,5,2},左[]={-1,-1,2,-1,3}和右[]={2,2,5,4,5}。请注意,我们使用-1表示左侧数组的越界位置,使用值5即N表示右侧的越界位置

看看我们的数组,我们看到3<右[2]和2>左[3],这意味着区间{a[2],a[3]}即{1,5}是一个几乎排序的区间

需要注意的一件有趣的事情是,一旦我们定义了left[]和right[]之后,我们就对数字进行了编码,并且它们之间存在着相互关系,这使得我们现在能够解决这个问题,只处理数组的索引,而不再处理组成数组的数字

左[]和右[]数组都可以按时计算

然后,我们可以使用这些数组有效地计算几乎已排序的间隔的总数。我们从右到左遍历索引。当我们在数组上迭代时,我们可以保留一个集合B,该集合B由所有可能的结束索引组成,用于从当前索引开始或在当前索引左侧开始的所有间隔

这可以通过将索引值i添加到索引i处的集合B中,并删除索引左[i]处的索引值i来实现,索引左[i]将始终是i左侧的某个索引。维护集合B可以在O1时间内完成

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

在索引i处,只有当i>left[j]时,索引j才会在B集合中。区间{a[i]…a[j]}是一个几乎排序的区间,如果j 这只剩下我们如何有效地计算B中小于正确[i]的索引数。这可以在OLOGN时间内使用二叉索引树来完成

因此,总运行时间将在*logn.

上,并且您已经分析了这段代码,以查看其位置
时间被占用?算法在^2上,无论您如何优化它,它都太慢了。你需要一个更好的算法。评测没有帮助如果有两个5,会发生什么?用谷歌搜索几乎排序的间隔,在第一次搜索命中时环顾四周,会发现一个挑战的作者。为什么会投反对票@user2357112我不想实现作者的解决方案!我想自己试一下,我确实想解决它。
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)])
(filter(None, seq)) = [(3,), (1,), (2,), (5,), (5,), (1, 2), (2, 5), (5, 5), (1, 2, 5), (2, 5, 5), (1, 2, 5, 5)]