在2个排序数组的并集中查找第k个最小元素

时间:2012-12-20 19:18:00

标签: algorithm

我认为这个问题被问了很多次,但仍然没有任何明确的解决方案! 无论如何,这是我在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 < 2:从 - A [0],A [1],B [0],B [1]返回第2个最小元素 其他: 比较A [k / 2]和B [k / 2]:如果A [k / 2]&lt; B [k / 2]:那么第k个最小元素将在A [1 ... n]和B [1 ... K / 2]中...好吧在这里我投掷k / 2(可以做类似的A [ k / 2]&gt; B [k / 2]。所以现在问题是下一次k指数是K还是k / 2?

我在做什么是对的?

1 个答案:

答案 0 :(得分:2)

那个算法并不糟糕 - 在我看来,它比SO上通常引用的算法更好,因为它更简单 - 但它有一个巨大的缺陷:它要求两个向量至少具有k个元素。 (问题是它们都具有相同数量的元素n,但从未指定n ≥ k;函数甚至不能告诉它向量有多大。但是,这很容易解决。我现在将它留作练习。一般来说,我们需要这样的算法来处理不同大小的数组,而且确实如此;我们只需要明确前提条件。)

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

好的,如果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)i中有A个元素< A[i]

2)j中有B个元素< B[j]

3)A[i] < B[j]

4)从(2)和(3),我们知道:

5)j中的B元素最多为< A[i]

6)从(1)和(5),我们知道:

7)i + jA中的B个元素最多只有< A[i]

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

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

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

现在,让我们检查前提条件。我们说AB都有一个元素k元素,所以它们都至少有k + 1个元素。在新问题中,我们想知道缩短的A和原始的B是否至少有k - i个元素。显然B会这样做,因为k - i不是k。另外,我们从i + 1中删除了A个元素。最初它至少有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,这涉及比较四个元素而不是两个元素。对于它的价值,我相信&#34;找到两个排序向量中的第二个最小元素的最佳解决方案&#34;是:min(max(A [0],B [0]),min(A [1],B [1])),这需要三次比较。这不会使他们的算法变慢;更复杂。

[3]假设元素可以重复。实际上,这并没有改变任何事情。该算法仍然有效。为什么?好吧,我们可以假装A中的每个元素实际上都是一个具有实际值和实际索引的元素,对于B中的每个元素都是如此,并且当我们使用索引作为平局判断时比较矢量中的值。在向量之间,如果A,我们优先考虑A[i] ≤ B[j]中的所有元素;否则B中的所有元素。这实际上并没有真正改变实际的代码,因为我们实际上从来没有做过任何不同的比较,但它使证明中的所有不等式都有效。