【问题标题】:Why is this code failing a test case [Max Distance]为什么此代码未能通过测试用例 [Max Distance]
【发布时间】:2022-01-01 08:03:28
【问题描述】:

问题: 给定一个整数数组 A,求 j - i 在 A[i]

如果没有可能的解决方案,则返回 -1。

示例:

A : [3 5 4 2] 输出:对 (3, 4) 为 2

输入: 9 8 7 -9 -1

预期输出: 1

我的输出: 0

我尝试的代码适用于除上述输入之外的所有情况,谁能解释为什么它会失败并为我提供更正的版本?

我的代码(Python):

class Solution:
    def maximumGap(self, A):
        # write your method here
        m=-1
        for i in range(len(A)-1):
            j=len(A)-i-1
            if(A[i]<=A[j]):
                m=max(m,j-i)
        return m

我尝试使用 2 个循环,它通过了上述情况,但另一个则超出了时间限制。

        m=-1
        for i in range(len(A)):
            for j in range(len(A)): 
                if(A[i]<=A[j]):
                    m=max(m,j-i)
        return m

【问题讨论】:

  • (3,5) [index 0-1] 可能是其中一种情况,但我们需要最大化问题中的距离,因为距离 (3,4) [index 0 -2] 考虑。

标签: python arrays prefix-sum


【解决方案1】:

你有一个非常有趣的问题!我受到它的启发,实现了非常快速、几乎线性的时间解决方案。我下面的算法使用排序,运行时间以整个数组的单次排序速度为主,所以它的运行时间为O(N * Log2(N)),其中N是数组中的元素数。

虽然我的算法比其他解决方案更复杂,但与运行时间为O(N^2) 的其他二次解决方案相比,它在更大的N 上实现了更快的速度,OP 的问题中提供了一种这样的二次解决方案。

在我的算法中,我们执行以下步骤:

  1. Arg 排序输入数组a。在计算机科学中,arg-sort 意味着找到使数组排序的索引顺序。换句话说,如果我有数组a,那么 arg-sort 会找到索引数组sort_idxs,这样数组a[sort_idxs[i]] 就会针对所有0 &lt;= i &lt; len(a) 进行排序。这一步是通过带有 key = lambda... 参数的常规 sorted() 内置函数完成的。

  2. 查找 arg-sort 索引的反向,即查找索引数组sort_idxs_rev,使得所有0 &lt;= i &lt; len(a)sort_idxs_rev[sort_idxs[i]] = i。此步骤在时间上是线性的,即花费O(N) 时间。

  3. begin_kmax_dist 设置为都保持值0。向后遍历0 &lt;= j &lt; len(a) 范围内的所有j。迭代时,执行步骤4.-5.。步骤4.-5. 需要线性时间O(N)

  4. begin_k &lt;= k &lt; sort_idxs_rev[j] 范围内为k 找到所有sort_idxs[k] 的最小值(表示为i)。

  5. 更新max_dist,这是产生的最大距离,如果它大于以前的max_dist,则更新它以保持新值j - i。更新begin_k 以保留sort_idxs_rev[j] + 1

  6. 输出结果 max_dist 作为答案。

上述算法说明:

我们可以观察到,如果我们取最右边的值a[j],那么对于所有a[i] &lt;= a[j],如果最大距离更小,我们可以将最大距离更新为j - "(minimal such i)"(并且最大距离在开始时为0)。

之后,我们可以从进一步的计算中删除数组元素a[i],例如a[i] &lt;= a[j],因为没有其他更小的j 会给所有a[i] 提供更大的距离,例如a[i] &lt;= a[j]。如果其他一些j0 这样j0 &lt; j 会给出更大的距离,这意味着j - min_i &lt; j0 - min_i,因此j &lt; j0,但我们采用j0,这样j0 &lt; j 因此矛盾。

i 索引中查找最小元素需要线性时间O(count_i),而且这些i 已从进一步计算中删除,这意味着后续步骤将花费O(N - count_i) 时间,因此总时间为O(count_i) + O(N - count_i) = O(N)

我们可以通过使用先前计算的 arg-sort 和反向 arg-sort 索引找到所有小于 a[j] 的元素。因此,找到更小的元素在时间上是线性的。

所以每个j 都会删除一堆小于a[j]a[i] 元素。它还将最大距离更新为此类j 的最大可能距离。

当我们从右到左迭代所有j 时,这意味着我们观察每个j 对于这个j 的最大可能距离。并且所有j 的所有最大距离的最大值将是最终解决方案,因为如果存在解决方案,则意味着它在某个j = j_sol 点实现,但是因为我们观察到所有j,所以这意味着我们也观察到@ 987654386@ 及其对应的最大距离答案。

j 的每次迭代中,我们删除了一堆a[i],我们将它们从进一步的观察中删除。这意味着在每次迭代中,数组变得越来越短。每次迭代需要线性时间O(count_i) 来找到最小的i,其中count_i 是删除的i 索引的数量。由于每次迭代都会删除相同数量的count_i 并花费时间O(count_i) 来找到最小值,因此j-loop 的总运行时间是O(count_i_0) + ... + O(count_i_N) = O(N),因为所有count_i 总和等于N

当然实际上删除数组元素a[i] 会很慢,因为Python 的列表是这样实现的,即删除列表中间的元素需要很多时间,实际上是O(N) 时间。所以在我下面的代码中,而不是实际删除元素,我只是在每次迭代时将begin_k 增加count_i 的数量,这种方式我模拟删除元素,因为从排序数组中删除元素只是意味着保留一些指向开头的指针范围,直到该指针所有内容都被视为“已删除”,因此我保留了这样的begin_k(逐渐增长count_i),它表示排序数组中的一个点,在此之前所有内容都被视为已删除。

所以 arg-sort 花费了大部分时间,这仍然非常快,O(N * Log2(N)),因为 Python 中的排序是在这段时间内实现的。反向 arg-sort 采用 O(N)。然后j 循环的总时间也需要O(N)。因此总运行时间主要取决于排序算法的速度。

如果输入数组真的非常庞大,比如数十亿个元素,那么我的算法将击败所有运行时间为 O(N^2) 的二次算法。当然,要处理数十亿个数组元素,必须使用C++ 而不是 Python。在 C++ 中对数十亿个元素进行排序仍然很快,需要十几秒。

在我的代码中,如果您想从控制台获取输入,您可以将第一行从 input_ = '3 5 4 2' 更改为 input_ = input()3 5 4 2 在代码中用作固定输入只是为了可运行的自包含示例,Stack-Overflow 的每个访问者都可以在没有外部依赖的情况下运行。最终答案打印到控制台输出。

完整代码如下:

Try it online!

# Input data
#input_ = '9 8 7 -9 -1'
input_ = '3 5 4 2' # input()
a = list(map(int, input_.split()))

# Arg-sort input array
sort_idxs = sorted(range(len(a)), key = lambda i: (a[i], i))

# Compute reverse of arg-sort indices
sort_idxs_rev = [0] * len(a)
for i0, i1 in enumerate(sort_idxs):
    sort_idxs_rev[i1] = i0
    
begin_k = 0
max_dist = 0

# Linearly search for the answer
for j in range(len(a) - 1, -1, -1):
    end_k = sort_idxs_rev[j]
    if begin_k >= end_k:
        continue
    i = min(sort_idxs[k] for k in range(begin_k, end_k))
    max_dist = max(max_dist, j - i)
    begin_k = end_k + 1
    if begin_k >= len(a):
        break

# Output answer
print(max_dist)

输入:

3 5 4 2

输出:

2

【讨论】:

    【解决方案2】:

    您不需要测试每一对。从末尾开始搜索,直到找到当前元素为&gt;=的元素,这将是最大的差距。

    作为一项额外的优化,您可以保存该元素的 j 值,并在通过它时跳出外循环。

    m = -1
    maxj = len(A)
    for i, el in enumerate(A):
        if i > maxj:
            break
        for j in range(len(A)-1, -1, -1):
            if el <= A[j]:
                m = max(m, j-i)
                maxj = j
                break
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-25
      • 1970-01-01
      • 2021-09-05
      • 1970-01-01
      • 1970-01-01
      • 2022-08-08
      • 1970-01-01
      相关资源
      最近更新 更多