优化列表的子列表

时间:2017-11-05 21:46:54

标签: python algorithm python-3.x optimization

问题是从给定列表中找到不包含大于指定上限数量的数字的子列表总数right,子列表最大数量应大于下限绑定说left。假设我的列表是:x=[2, 0, 11, 3, 0],子列表元素的上限是10,下限是1,那么我的子列表可以是{{} 1}}因为子列表总是连续的。我的脚本运行良好并产生正确的输出但需要一些优化

[[2],[2,0],[3],[3,0]]

def query(sliced,left,right): end_index=0 count=0 leng=len(sliced) for i in range(leng): stack=[] end_index=i while(end_index<leng and sliced[end_index]<=right): stack.append(sliced[end_index]) if max(stack)>=left: count+=1 end_index+=1 print (count) origin=[2,0,11,3,0] left=1 right=10 query(origin,left,right)

列表说output:4个有效子列表可以x=[2,0,0,1,11,14,3,5]总计为10

2 个答案:

答案 0 :(得分:3)

蛮力

生成每个可能的子列表,并检查给定的条件是否适用于每个子列表。

最坏情况:对于数组中的每个元素eleft < e < right。 时间复杂度:O(n^3)

优化蛮力(OP代码)

对于数组中的每个索引,逐步构建一个有效的临时列表(尽管不是真的需要)。

最坏情况:对于数组中的每个元素eleft < e < right。 时间复杂度:O(n^2)

更优化的解决方案

如果数组包含n个元素,则数组中的子列表数为1 + 2 + 3 + ... + n = (n * (n + 1)) / 2 = O(n^2)。我们可以战略性地使用这个公式。

首先,正如@Tim所提到的,我们可以通过将列表中的大于right的数字分区来考虑不包含任何大于right的数字的子列表的总和。这将任务简化为仅考虑具有小于或等于right的所有元素的子列表,然后对答案求和。

接下来,通过将缩小的子列表分割为大于或等于left的数字,拆分缩小的子列表(是的,子列表的子列表)。对于每个子列表,计算子列表子列表的可能子列表的数量(如果子列表具有长度k * (k + 1) / 2,则为k)。对子列表的所有子列表完成后,将它们一起添加(例如,将它们存储在w中),然后计算该子列表的可能子列表的数量并减去{{ 1}}。

然后按总和汇总结果。

最坏情况:对于数组中的每个元素we

时间复杂度:e < left

我知道这很难理解,所以我已经包含了工作代码:

O(n)

答案 1 :(得分:1)

将数字分类为小,有效和大(S,V和L)并进一步索引有效数字:V_1,V_2,V_3等。让我们假设没有大数字开始。

考虑列表A = [S,S,...,S,V_1,X,X,X,X,... X]。如果V_1有索引n,则有n + 1,形式为[V_1]的子集,[S,V_1],[S,S,V_1]等。对于这些n + 1个子集中的每一个,我们可以附加len(A)-n-1序列:[X],[XX],[XXX]等。给出包含V_1的总共(n + 1)(len(A)-n)子集。 但是我们可以将所有子集的集合划分为包含V_k但没有V_n的那些子集,其中n小于k。因此,我们必须使用V_2和itterate在列表的剩余XXX ... X部分执行相同的计算。这需要这样的事情:

def query(sliced,left,right,total):
    index=0
    while index<len(sliced):
        if sliced[index]>=left:
            total+=(index+1)*(len(sliced)-index)
            return total+query(sliced[index+1:],left,right,0)
        else:
            index+=1
    return total

要合并大数字,我们可以根据大数字出现的位置对整个集合进行分区,并为每个数字添加序列总数。如果我们调用第一个函数sub_query,那么我们得到以下结果:

def sub_query(sliced,left,right,total):
    index=0
    while index<len(sliced):
        if sliced[index]>=left:
            total+=(index+1)*(len(sliced)-index)
            return total+sub_query(sliced[index+1:],left,right,0)
        else:
            index+=1
    return total

def query(sliced,left,right):
    index=0
    count=0
    while index<len(sliced):
        if sliced[index]>right:
            count+=sub_query(sliced[:index],left,right,0)
            sliced=sliced[index+1:]
            index=0
        else:
            index+=1
    count+=sub_query(sliced,left,right,0)
    print (count)

这似乎贯穿列表并检查最大/最小值的次数。请注意,它不区分原始列表中相同但来自不同位置的子列表(如[0,1,0,0,1,0]等列表所示。但是代码来自原帖也不会这样做,所以我猜这不是一个要求。