【问题标题】:Finding the kth-smallest element in union of sorted arrays查找排序数组联合中的第 k 个最小元素
【发布时间】:2012-09-23 20:00:02
【问题描述】:

我正在研究关于在leetcode 的两个排序数组的联合中查找第 k 个最小元素的文章。我不认为该算法是正确的。有这样一行: 我们观察到,当 Ai 。任何i 和j 怎么可能是真的?

其次,这句话也让我感到困惑:我们试图通过比较 A 和 B 的中间元素(我们将其识别为 Ai 和 Bj)来解决这个棘手的问题。如果 Ai 在 Bj 和 Bj-1 之间,我们刚刚找到了 i+j+1 个最小元素,尽管结果是正确的。谁能解释原因?我真的很想了解算法,我通过合并数组来完成它,但这需要O(N) 时间,而这里的O(log N) 时间。

【问题讨论】:

  • “排序数组的联合”,听起来像是merge 的merge sort 的一部分。
  • @AshwiniChaudhary:我认为这个想法是你得到两个排序后的数组,你找到第 k 个最小的数组而不合并它们,因此采用 O(log n) 而不是 O(n)。
  • 没错。这是解决此问题的常用方法,但与链接文章算法中的O(log N) 时间相比,它产生O(N) 时间。

标签: python algorithm


【解决方案1】:

您是在孤立地解释这些陈述,但它们是相互依存的。这是(我认为)您所指的文本:

保持不变 i + j = k – 1, 如果 Bj-1

我们观察到,当 Ai

将其分解为子命题会产生以下解释(请记住,索引从0 开始,但A0 是第一个最小的项目,A1 是第二个最小的项目,以此类推):

  1. i + j = k - 1(根据定义,不变)
  2. 假设Bj-1 < Ai < Bj。那么Ai 必须是最小的k。这是因为Ai 大于A 中的i 项,并且大于B 中的j 项。所以它大于i + j = k - 1 项的总数。这意味着它在合并的A|B 列表中的index 将是k - 1,因此它将是该列表中的kth 项。
  3. 假设Ai-1 < Bj < Ai。那么Bj 必须是最小的k,根据 2 中的相同推理。
  4. 现在假设 (a) Bj-1 < Ai < Bj 和 (b) Ai-1 < Bj < Ai 都是错误。那么很明显,如果Ai < Bj 那么A1 < Bj-1,因为否则(a)将是真的。同样,如果Bj < Ai 则为Bj < Ai-1,否则,(b) 将为真。

我相信你的话,你想要对这些陈述而不是整个算法的解释。 (但如果你愿意,我会说更多。)

还要注意,正如Daniel Fischer 的回答提醒我的那样,上述推理仅在没有重复的情况下成立;称这个命题为 0。

【讨论】:

  • 等一下,让我看看够不够!顺便说一句,谢谢!
  • 但是如果4中的两个断言是错误的,那么也有可能不是Ai < Bj和A1 < Bj-1,而是Ai > Bj?
  • 是的,我相信是的。要点很简单,如果上面的 (a) 和 (b) 都是错误的,那么 Ai 要么高于 Bj 和 Bj-1,要么低于两者; Bj 也是如此。
  • 那么,如果两者都可能,那么算法怎么会基于只有一个可能的事实呢?
  • 我不明白它是如何依赖于只有一个可能的这一事实的。我想它是基于这样一个事实,如果Ai < Bj-1 < Bj,那么Bj > Ai > Ai-1,反之亦然。所以换句话说,两者都是可能的,但如果一个对 Ai 成立,那么另一个对 Bj 成立。
【解决方案2】:

我们观察到,当Ai < Bj 时,Ai < Bj-1 一定是真的。另一方面,如果Bj < Ai,那么Bj < Ai-1.. 任何i 和j 怎么可能是真的?

并非所有 i 和 j 对都是如此。文章考虑了一种特殊情况。

首先,假设没有重复,即使是A 和B 的共同元素的形式。二、结论

Ai < Bj ==> Ai < Bj-1,   resp.  Bj < Ai ==> Bj < Ai-1

是在都没有

的条件下制作的
Bj-1 < Ai < Bj  resp. Ai-1 < Bj < Ai

持有。因此,通过排除这些配置,Ai &lt; Bj ==&gt; Ai &lt;= Bj-1 和 Bj &lt; Ai ==&gt; Bj &lt;= Ai-1 立即紧随其后,严格不等式紧随其后,假设不存在重复。

我们尝试通过比较 A 和 B 的中间元素(我们将其识别为 Ai 和 Bj)来解决这个棘手的问题。如果Ai在Bj和Bj-1之间,我们刚刚找到了第i+j+1个最小元素

在数组B中,有j元素小于Bj,在数组A中,有i元素小于Ai(索引从0开始)。所以如果Bj-1 &lt; Ai &lt; Bj,两个数组一起正好包含小于Ai的j + i元素。

如果有重复,会发生什么变化?

不多。

我们还是考虑i + j = k-1的情况。让我们假设Ai &lt;= Bj。

  1. 如果Ai = Bj怎么办?
  2. 如果Ai &lt; Bj怎么办?

在情况 1. 中,设m 是满足Am = Ai 的最小索引,而n 是满足Bn = Bj 的最小索引。然后在两个数组中,恰好有严格小于Ai 的m + n &lt;= i + j = k-1 元素,并且至少有不大于Ai 的(i+1) + (j+1) = (k+1) 元素。因此第 k 个最小的元素等于Ai。

对于 2.,我们需要考虑三种情况,a)Bj-1 &lt; Ai,b)Bj-1 = Ai,c)Bj-1 &gt; Ai。

在情况 a) 中,B 中的 j 元素不大于 Ai,并且它们都严格小于,A 中的 m &lt;= i 元素严格小于Ai(m 同上)和一个未知数,但至少 i-m+1 元素等于 Ai。所以两个数组中恰好有j + m &lt;= j + i = k-1元素一起严格小于Ai,并且至少j + m + (i-m+1) = j+i+1 = k元素不大于Ai,因此两个数组的第k个最小元素一起等于@ 987654373@.

在情况 b) 中,同样的推理表明两个数组的第 k 个最小元素加起来等于 Ai。

在剩下的情况下,Ai &lt; Bj-1,事情变得几乎没有更复杂。数组B 至少包含不大于Bj-1 的j 元素,数组A 至少包含严格小于Bj-1 的i+1 元素,因此两个数组的第k 个最小元素加在一起为最大为Bj-1。但它不能小于Ai(B 最多包含小于Ai 的元素j-1,因此两个数组一起最多包含小于i + (j-1) = k-2 的元素Ai)。

所以我们仍然可以从数组A 中丢弃Ai 以下的部分,从数组B 中丢弃Bj-1 以上的部分,并继续进行,没有重复。

所有的改变是一些严格的不等式必须被弱不等式取代。

代码(如果传递起始索引和长度而不是切片,效率会更高,但切片会产生更短的代码):

def kthsmallest(A, B, k):
    if k < 1:
        return None
    a_len, b_len = len(A), len(B)
    if a_len == 0:
        return B[k-1] # let it die if B is too short, I don't care
    if b_len == 0:
        return A[k-1] # see above
    # Handle edge case: if k == a_len + b_len, we would
    # get an out-of-bounds index, since i + j <= a_len+b_len - 2
    # for valid indices i and j
    if a_len + b_len == k:
        if A[-1] < B[-1]:
            return B[-1]
        else:
            return A[-1]
    # Find indices i and j approximately proportional to len(A)/len(B)
    i = (a_len*(k-1)) // (a_len+b_len)
    j = k-1-i
    # Make sure the indices are valid, in unfortunate cases,
    # j could be set to b_len by the above
    if j >= b_len:
        j = b_len-1
        i = k-1-j
    if A[i] <= B[j]:
        if j == 0 or B[j-1] <= A[i]:
            return A[i]
        # A[i] < B[j-1] <= B[j]
        return kthsmallest(A[i:], B[:j], k-i)
    # B[j] < A[i], symmetrical to A[i] < B[j]
    if i == 0 or A[i-1] <= B[j]:
        return B[j]
    # B[j] < A[i-1]
    return kthsmallest(A[:i], B[j:], k-j)

【讨论】:

  • 如果数组本身包含重复元素以及它们之间的重复元素(这些常见的重复元素本身可能在单个数组中重复),算法将如何受到影响?
  • 看起来很复杂。我很难理解这个逻辑,即使你解释得很清楚。你能建议一个不同的算法吗?你认为它很复杂,还是我太笨了,无法理解?
  • 我已经添加了代码,看起来是不是太复杂了(结合解释)?
  • 这需要 一些 时间才能进入我的脑海。我的错,我才刚刚开始习惯复杂的算法。
  • 是的,切片会增加额外的开销,因此应该使用索引来完成。这已经够复杂了,天知道切片换成索引会变成什么样子!
猜你喜欢
  • 1970-01-01
  • 2017-03-04
  • 1970-01-01
  • 2021-01-13
  • 1970-01-01
  • 2014-07-21
  • 2013-02-17
相关资源
最近更新 更多