【问题标题】:Dividing array into two parts. Need better solution将数组分成两部分。需要更好的解决方案
【发布时间】:2014-11-30 16:08:53
【问题描述】:

给定一个由 1 和 0 组成的数组,将数组分成几部分,使第一个数组中 1 的数量等于另一个数组中 0 的数量。打印出现此分区的索引。如果有很多这样的可能性,找到第一个这样的分区。

这里的示例代码在下面用 python 给出。它打印的答案是 7。这意味着第一个数组直到 a[7]。第二个数组从a[8] 开始直到结束。

这个算法工作得很好,但我认为复杂度是 O(n^2)。如果可能的话,我想要一个更好的解决方案。任何帮助都会很棒。

def findParts(arr):
    length=len(arr)
    count_ones = 0
    count_zeros = 0
    index=0

    for index in range(length-1):
        if arr[index] == 1:
            count_ones+=1
        for j in range(index+1, length-1):
            if arr[j] == 0:
                count_zeros+=1

    if (count_ones-count_zeros == 0):
        return index
    else:
        count_zeros=0;

a=[1,1,0,0,1,0,0,0,1,0,1,1,0,1,0,1,1]
print findParts(a)

【问题讨论】:

  • 这是您使用的实际缩进吗?因为那是行不通的。
  • 它为我返回 None !并且由于您没有嵌套订单是O(n)
  • @khelwood:不。:) 在 stackoverflow 中格式化对我来说是个问题。我只是确保它看起来不错。

标签: python arrays algorithm optimization partitioning


【解决方案1】:

以下是线性时间算法,同时证明了解决方案的存在以及该解决方案是唯一可能的解决方案:

lst = [1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1]

index = 0
leftCount, rightCount = 0, lst.count(0)
while leftCount < rightCount:
    if lst[index] == 1:
        leftCount += 1
    else:
        rightCount -= 1
    index += 1

print(lst[:index], index, lst[index + 1:])

这将从索引 0 开始,左分区为空,使左分区 0 中的 1 计数。我们在 O(n) 中计算右分区中的 0。

现在,我们迭代地将索引或分隔线向右移动。我们这样做,只要 leftCount 仍然低于 rightCount。如果情况发生变化,我们已经找到了解决方案(因为两个计数将相等)。当我们向右移动时,我们查看列表元素。如果是1,我们将左数加一;如果是0,我们从正确的计数中减去一个。最终,我们将达到两个计数相等的完美索引。

由于我们在每次迭代中只改变 一个 的计数,所以总会有这样的解决方案。而且由于进一步迭代会使余额再次失效,这也证明了只能有一个单一解决方案。

至于时间复杂度,我们对列表进行一次迭代以计算零,然后在移动边界时对其进行第二次迭代。所以这是 O(n)。

【讨论】:

  • 谢谢。这太棒了!
  • 实际上,如果你从中间而不是从一端开始迭代(见下面我的回答),你会在 3/4 的情况下更快地找到解决方案(假设分区的随机分布网站)。
  • @striving_coder 我的目的不是为了获得最高效的代码,而是为了我的目的展示一个清晰简单的算法。我相信您可以对此进行微调。话虽如此,我不认为您的解决方案实际上更快,因为您需要将列表拼接两次。 timeit 在我的机器上确认了这一点。
  • @poke:我认为我计算列表的两半所需要的操作数与计算整个列表的操作数完全相同。如果切片本身有很大的开销,它可以很容易地重写为对数组部分的简单迭代。
【解决方案2】:

从位于中心元素的分隔线开始,计算数组左侧的 1 总数 (#(1)) 和右侧部分 (#(0)) 的 0 总数。

如果#(1) &lt; #(0) 将分隔线向右移动一个元素并相应地更新#(0)#(1)(基于前一个分隔线的值和新分隔线的值)。

如果#(1) &gt; #(0) 将分隔线向左移动一个元素并相应地更新#(0)#(1)

您将在O(n) 中找到分隔符。

您可以事先检查是否存在解决方案,以防止无限循环(如果没有解决方案,即数组全为 0,全为 1)。

代码如下:

import math

def findParts(arr):
    length = len(arr)
    index = int(math.ceil(length/2.0))
    count_ones = arr[:index+1].count(1)
    count_zeros = arr[index+1:].count(0)
    if count_ones == 0 or count_ones == length:
        return -1

    while count_ones != count_zeros:
        if count_ones < count_zeros:
            index+=1
            if arr[index] == 1:
                count_ones+=1
            else:
                count_zeros-=1
        else:
            if arr[index] == 1:
                count_ones-=1
            else:
                count_zeros+=1
            index-=1

    return index


a=[1,1,0,0,1,0,0,0,1,0,1,1,0,1,0,1,1]
print findParts(a)

【讨论】:

  • 由于主体已经是 O(n),并且我们正在使用 1 和 0,因此可以通过另一个调用来检查无解的情况,以计算整个数组中 1 的数量.如果 count==len(array) 或 count == 0 则无解
猜你喜欢
  • 1970-01-01
  • 2011-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多