我认为这个问题被问了很多次,但仍然没有任何明确的解决方案! 无论如何,这是我在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?
我在做什么是对的?
答案 0 :(得分:2)
那个算法并不糟糕 - 在我看来,它比SO上通常引用的算法更好,因为它更简单 - 但它有一个巨大的缺陷:它要求两个向量至少具有k
个元素。 (问题是它们都具有相同数量的元素n
,但从未指定n ≥ k
;函数甚至不能告诉它向量有多大。但是,这很容易解决。我现在将它留作练习。一般来说,我们需要这样的算法来处理不同大小的数组,而且确实如此;我们只需要明确前提条件。)
使用floor
和ceil
很好而且具体,但可能令人困惑。让我们以最一般的方式来看待这个问题。此外,引用的解决方案似乎假设数组是1索引的(即A[1]
是第一个元素,而不是A[0]
)。然而,我即将编写的描述使用更像C的伪代码,因此它假定A[0]
是第一个元素。因此,我要编写它以在组合集中找到元素k
,即(k+1)th
元素。最后,我要描述的解决方案与所提出的解决方案略有不同,这在最终条件下将是显而易见的。恕我直言,它稍好一点。
好的,如果x
是序列中的元素k
,则序列中的k
元素确实小于x
。 (我们不会处理有重复元素的情况,但它没有太大区别。请参阅注释3.)
假设我们知道A
和B
每个都有一个元素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 + j
和A
中的B
个元素最多只有< A[i]
8)但是i + j
是k - 1
,所以实际上我们知道:
9)合并数组的元素k
必须大于A[i]
(因为A[i]
最多只是元素i + j
)。
由于我们知道答案必须大于A[i]
,我们可以通过A [i]丢弃A [0](实际上,我们只是增加一个数组指针,但实际上我们会丢弃它们)。但是,我们现在丢弃了原始问题中的i + 1
个元素。因此,在新的元素集中(在缩短的A
和原始的B
中),我们需要元素k - (i + 1)
,而不是元素k
。
现在,让我们检查前提条件。我们说A
和B
都有一个元素k
元素,所以它们都至少有k + 1
个元素。在新问题中,我们想知道缩短的A
和原始的B
是否至少有k - i
个元素。显然B
会这样做,因为k - i
不是k
。另外,我们从i + 1
中删除了A
个元素。最初它至少有k + 1
个元素,所以现在它至少有k - i
个元素。所以我们就在那里。
最后,让我们检查终止条件。在开始时我说我们选择非负整数i
和j
以便i + j == k - 1
。如果k == 0
,这是不可能的,但可以为k == 1
完成。所以我们只需要在k
达到0时做一些特殊的事情,在这种情况下我们需要做的就是返回min(A[0], B[0])
。 [这是比你看到的算法更简单的终止条件,见注2]。
那么选择i
的好策略是什么?我们最终会从问题中删除i + 1
或k - i
个元素,我们希望它尽可能接近一半的元素。所以我们应该选择i = floor((k - 1) / 2)
。虽然可能不会立即明显,但这会产生j = floor(k / 2)
。
我忽略了解决A
和B
元素较少的情况。它并不复杂;我鼓励你自己思考一下。
[1]您正在查看的算法选择i + j == k
(如果k
为偶数),并删除i
或j
元素。我选择i + j == k - 1
(总是),这可能会使其中一个更小,但随后会删除i + 1
或j + 1
个元素。所以它应该稍微快速收敛。
[2]选择i + j == k
(他们的)和i + j == k - 1
(我的)之间的区别在最终条件中是显而易见的。在他们的公式中,i
和j
都必须是正数,因为如果其中一个为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
中的所有元素。这实际上并没有真正改变实际的代码,因为我们实际上从来没有做过任何不同的比较,但它使证明中的所有不等式都有效。