该算法还不错——在我看来,它比 SO 上通常引用的算法要好,因为它要简单得多——但它有一个巨大的缺陷:它要求两个向量至少具有k 元素。 (问题是它们都有相同数量的元素,n,但从未指定n ≥ k;该函数甚至不允许您告诉它向量有多大。但是,这很容易解决。我暂时将其作为练习。一般来说,我们需要一个这样的算法来处理不同大小的数组,它确实如此;我们只需要弄清楚先决条件。)
floor 和 ceil 的使用很好且具体,但可能会令人困惑。让我们以最一般的方式来看这个。此外,引用的解决方案似乎假设数组是 1 索引的(即A[1] 是第一个元素,而不是A[0])。然而,我将要写的描述使用了一个更像 C 的伪代码,所以它假定 A[0] 是第一个元素。因此,我将编写它以在组合集中查找元素k,即(k+1)<sup>th</sup> 元素。最后,我要描述的解决方案与提出的解决方案略有不同,这在最终条件下会很明显。恕我直言,它稍微好一点。
好的,如果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) 在A 中有i 元素,它们是< A[i]
2) 在B 中有j 元素是< B[j]
3)A[i] < B[j]
4) 由 (2) 和 (3) 可知:
5) B 中最多有j 元素,即< A[i]
6) 由 (1) 和 (5) 可知:
7) A 和 B 中最多有 i + j 元素,它们是 < A[i]
8) 但是i + j 是k - 1,所以实际上我们知道:
9) 合并数组的元素k必须大于A[i](因为A[i]最多是元素i + j)。
因为我们知道答案必须大于A[i],所以我们可以丢弃 A[0] 到 A[i](实际上,我们只是增加一个数组指针,但实际上我们会丢弃它们)。但是,我们现在已经从原始问题中丢弃了 i + 1 元素。所以在新的元素集合中(在缩短的A 和原始的B 中),我们需要元素k - (i + 1),而不是元素k。
现在,让我们检查前提条件。我们说过A 和B 都有一个元素k 开始,所以它们都至少有k + 1 元素。在新问题中,我们想知道缩短的A 和原始的B 是否都至少有k - i 元素。显然B 确实如此,因为k - i 不大于k。此外,我们从A 中删除了i + 1 元素。最初它至少有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,这涉及比较四个元素,而不是两个元素。对于它的价值,我相信“从两个排序的向量中找到第二小的元素”的最佳解决方案是: min(max(A[0], B[0]), min(A[1], B[1 ])),这需要三个比较。这不会使他们的算法变慢;只是更复杂。
[3] 假设元素可以重复。其实这并没有改变什么。该算法仍然有效。为什么?好吧,我们可以假设A 中的每个元素实际上是一对具有其实际值和实际索引的对,对于B 中的每个元素也是如此,并且在比较向量。在向量之间,如果A[i] ≤ B[j],我们优先考虑A中的所有元素;否则B 中的所有元素。这实际上并没有改变实际的代码,因为我们实际上不必进行任何不同的比较,但它使证明中的所有不等式都有效。