【问题标题】:Finding kth smallest element in union of 2 sorted array在 2 个排序数组的联合中查找第 k 个最小元素
【发布时间】:2015-09-24 03:44:53
【问题描述】:

我认为这个问题被问了很多次,但仍然没有任何明确的解决方案! 无论如何,这就是我在 O(k) 中找到的好答案(也可能是 O(logm + logn))。但我不明白,如果 M_B > M_A(或其他方式)我们应该在 M_B 之后丢弃元素。但在这里它的反向投掷元素在 M_B 之前。谁能解释一下为什么?

http://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15451-s01/recitations/rec03/rec03.ps

还有一个问题是 K/2 ......我们应该这样做,但这对我来说并不明显。

[编辑 1]

Example
A = [2, 9, 15, 22, 24, 25, 26, 30]
B = [1, 4, 5, 7, 18, 22, 27, 33]
k= 6

Answer is 9 (A[1])

这就是我的想法,如果我想在 O(Log k) 中求解……每次都需要抛出 k/2 个元素。 基本解决方案:如果 K B[k/2] 做类似的事情。所以现在的问题是下一次 k 索引也是 K 或 k/2 ?

我做的对吗?

【问题讨论】:

  • 当你对这两个排序数组进行联合时,联合的结果是什么,或者你正在查看一个排序块后面跟着另一个排序块,与第一个没有共同之处?
  • 可以说两个数组中没有任何重复元素。和“联合”一样,合并排序! (这里不做归并排序,找第k个最小的)
  • 所以先出现数组1,然后出现数组2。数组1是排序的,数组2是排序的,但是两者加起来没有排序,两者之间没有共同的元素。对吗?
  • 是的,一起没有排序,在任何数组中都没有共同点
  • 如果您将不理解的内容放入问题本身而不是作为一个外部 postscript 文件。

标签: algorithm


【解决方案1】:

该算法还不错——在我看来,它比 SO 上通常引用的算法要好,因为它要简单得多——但它有一个巨大的缺陷:它要求两个向量至少具有k 元素。 (问题是它们都有相同数量的元素,n,但从未指定n ≥ k;该函数甚至不允许您告诉它向量有多大。但是,这很容易解决。我暂时将其作为练习。一般来说,我们需要一个这样的算法来处理不同大小的数组,它确实如此;我们只需要弄清楚先决条件。)

floorceil 的使用很好且具体,但可能会令人困惑。让我们以最一般的方式来看这个。此外,引用的解决方案似乎假设数组是 1 索引的(即A[1] 是第一个元素,而不是A[0])。然而,我将要写的描述使用了一个更像 C 的伪代码,所以它假定 A[0] 是第一个元素。因此,我将编写它以在组合集中查找元素k,即(k+1)<sup>th</sup> 元素。最后,我要描述的解决方案与提出的解决方案略有不同,这在最终条件下会很明显。恕我直言,它稍微好一点。

好的,如果x是序列中的元素k,那么序列中恰好有k元素小于x。 (有重复元素的情况我们不处理,但差别不大,见注3。)

假设我们知道AB 都有一个元素k。 (记住,这意味着它们每个都至少有k + 1 元素。)选择任何小于k的非负整数;我们称之为i。让j 成为k - i - 1(这样i + j == k - 1)。 [参见下面的注释 1。] 现在,查看元素 A[i]B[j]。假设A[i] 更小,因为在其他情况下我们只需要更改所有名称。请记住,我们假设所有元素都是不同的。所以这是我们目前所知道的:

1) 在A 中有i 元素,它们是&lt; A[i]

2) 在B 中有j 元素是&lt; B[j]

3)A[i] &lt; B[j]

4) 由 (2) 和 (3) 可知:

5) B 中最多有j 元素,即&lt; A[i]

6) 由 (1) 和 (5) 可知:

7) AB 中最多有 i + j 元素,它们是 &lt; A[i]

8) 但是i + jk - 1,所以实际上我们知道:

9) 合并数组的元素k必须大于A[i](因为A[i]最多是元素i + j)。

因为我们知道答案必须大于A[i],所以我们可以丢弃 A[0] 到 A[i](实际上,我们只是增加一个数组指针,但实际上我们会丢弃它们)。但是,我们现在已经从原始问题中丢弃了 i + 1 元素。所以在新的元素集合中(在缩短的A 和原始的B 中),我们需要元素k - (i + 1),而不是元素k

现在,让我们检查前提条件。我们说过AB 都有一个元素k 开始,所以它们都至少有k + 1 元素。在新问题中,我们想知道缩短的A 和原始的B 是否都至少有k - i 元素。显然B 确实如此,因为k - i 不大于k。此外,我们从A 中删除了i + 1 元素。最初它至少有k + 1 元素,所以现在它至少有k - i 元素。所以我们没问题。

最后,让我们检查终止条件。一开始我说我们选择非负整数ij这样i + j == k - 1。如果k == 0,这是不可能的,但可以为k == 1 完成。所以我们只需要在k达到0时做一些特殊的事情,在这种情况下我们需要做的是返回min(A[0], B[0])。 [这是一个比你看到的算法更简单的终止条件,见注 2。]

那么选择i 的好策略是什么?我们最终会从问题中删除i + 1k - i 元素,我们希望它尽可能接近一半的元素。所以我们应该选择i = floor((k - 1) / 2)。虽然它可能不会立即显而易见,但这会让j = floor(k / 2)

我忽略了解决AB 元素较少的情况。这并不复杂;我鼓励您自己考虑一下。


[1] 您正在查看的算法选择 i + j == k(如果 k 是偶数),并删除 ij 元素。我的选择i + j == k - 1(总是)这可能会使其中一个更小,但随后它会丢弃i + 1j + 1 元素。所以它应该收敛得更快一些。

[2] 选择i + j == k(他们的)和i + j == k - 1(我的)之间的区别在最终条件中很明显。在他们的公式中,ij 都必须是正数,因为如果其中一个为 0,则存在丢弃 0 个元素的风险,这将是一个无限递归循环。所以在他们的公式中,k 的最小可能值为 2,而不是 1,因此他们的终止案例必须处理 k == 1,这涉及比较四个元素,而不是两个元素。对于它的价值,我相信“从两个排序的向量中找到第二小的元素”的最佳解决方案是: min(max(A[0], B[0]), min(A[1], B[1 ])),这需要三个比较。这不会使他们的算法变慢;只是更复杂。

[3] 假设元素可以重复。其实这并没有改变什么。该算法仍然有效。为什么?好吧,我们可以假设A 中的每个元素实际上是一对具有其实际值和实际索引的对,对于B 中的每个元素也是如此,并且在比较向量。在向量之间,如果A[i] ≤ B[j],我们优先考虑A中的所有元素;否则B 中的所有元素。这实际上并没有改变实际的代码,因为我们实际上不必进行任何不同的比较,但它使证明中的所有不等式都有效。

【讨论】:

  • 时间复杂度是 O(logk) 还是 O(logm + logn)?(顺便说一句,很好的解释 +1)
  • O(log k)。 n 和 m 都不相关,只要它们大于 k。
猜你喜欢
  • 1970-01-01
  • 2014-07-21
  • 2017-03-04
  • 1970-01-01
  • 1970-01-01
  • 2021-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多