可能重复:
How to find the kth smallest element in the union of two sorted arrays?
这是一个问题,我的一位朋友告诉我他在面试时被问到,我一直在考虑解决方案。
次线性时间对我来说意味着对数,所以也许是某种分而治之的方法。为简单起见,假设两个数组的大小相同,并且所有元素都是唯一的
答案 0 :(得分:16)
我认为这是子阵列A[0..n-1]
和B[0..n-1]
上的两个并发二进制搜索,即O(log n)。
A[n-1]
之前或A
处,如果它在数组B[n-1]
中,或B
如果它在数组a
A
中索引b
的项目和B
中索引a + b > n
的项目。A[a] > B[b]
,则缩小搜索集
b = b / 2
然后a = a / 2
,则a + b < n
A[a] > B[b]
,则增加搜索集
b = 3/2 * b
然后a = 3/2 * a
,则a
(在a
和之前的a + b = n
之间)max(A[a], B[b])
则 nth 最大为{{1}} 我认为最坏的情况是O(ln n),但无论如何肯定是次线性的。
答案 1 :(得分:7)
我相信您可以使用二进制搜索的变体解决此问题。该算法背后的直觉如下。让两个数组分别为A和B,为了简单起见,我们假设它们的大小相同(这不是必需的,正如您所见)。对于每个数组,我们可以构造并行数组Ac和Bc,使得对于每个索引i,Ac [i]是两个数组中不大于A [i]且Bc [i]是的数量。两个数组中不大于B [i]的元素。如果我们可以有效地构造这些数组,那么我们可以通过在Ac和Bc上进行二进制搜索来找到值k,从而有效地找到第k个最小元素。那个条目的A或B的相应条目是第k个最大元素。二进制搜索是有效的,因为两个数组Ac和Bc是排序的,我认为你可以很容易地说服自己。
当然,这种解决方案在次线性时间内不起作用,因为构造数组Ac和Bc需要O(n)时间。那么问题是 - 有什么方法可以隐式地构建这些数组?也就是说,我们可以确定这些数组中的值而不必构建每个元素吗?我认为使用这种算法答案是肯定的。让我们从搜索数组A开始,看它是否具有第k个最小值。我们知道第k个最小值不能出现在位置k之后的数组A中的数组中(假设所有元素都是不同的)。因此,让我们只关注数组A的前k个元素。我们将对这些值进行二分搜索,如下所示。从位置k / 2开始;这是数组A中第k / 2个最小的元素。现在在数组B中进行二进制搜索,找到B中最大的值小于该值,并查看它在数组中的位置;这是B中元素的数量小于当前值。如果我们将A和B中元素的位置相加,我们将两个数组中的元素总数小于当前元素。如果这正是k,我们就完成了。如果这小于k,那么我们递归到A的前k个元素的上半部分,如果这大于k,我们在k的第一个元素的下半部分递归,等等。最后,我们要么发现第k个最大的元素在数组A中,在这种情况下我们就完成了。否则,在阵列B上重复此过程。
此算法的运行时如下。对数组A的搜索对k个元素进行二进制搜索,这需要进行O(lg k)次迭代。每次迭代都需要花费O(lg n),因为我们必须在B中进行二进制搜索。这意味着此搜索的总时间为O(lg k lg n)。在数组B中执行此操作的时间是相同的,因此算法的净运行时间为O(lg k lg n)= O(lg 2 n)= o(n),这是次线性的
答案 2 :(得分:2)
这与柯克的答案完全相同。
让Find( nth, A, B )
成为返回第n个数字的函数,并且| A | + | B | &gt; = n。这是简单的伪代码,不检查其中一个数组是否小,少于3个元素。如果是小数组,则在较大数组中进行一次或两次二进制搜索就足以找到所需的元素。
Find( nth, A, B )
If A.last() <= B.first():
return B[nth - A.size()]
If B.last() <= A.first():
return A[nth - B.size()]
Let a and b indexes of middle elements of A and B
Assume that A[a] <= B[b] (if not swap arrays)
if nth <= a + b:
return Find( nth, A, B.first_half(b) )
return Find( nth - a, A.second_half(a), B )
它是log(|A|) + log(|B|)
,因为输入数组可以分别具有n个元素log(n)
复杂度。
答案 3 :(得分:0)
int[] a = new int[] { 11, 9, 7, 5, 3 };
int[] b = new int[] { 12, 10, 8, 6, 4 };
int n = 7;
int result = 0;
if (n > (a.Length + b.Length))
throw new Exception("n is greater than a.Length + b.Length");
else if (n < (a.Length + b.Length) / 2)
{
int ai = 0;
int bi = 0;
for (int i = n; i > 0; i--)
{
// find the highest from a or b
if (ai < a.Length)
{
if (bi < b.Length)
{
if (a[ai] > b[bi])
{
result = a[ai];
ai++;
}
else
{
result = b[bi];
bi++;
}
}
else
{
result = a[ai];
ai++;
}
}
else
{
if (bi < b.Length)
{
result = b[bi];
bi++;
}
else
{
// error, n is greater than a.Length + b.Length
}
}
}
}
else
{
// go in reverse
int ai = a.Length - 1;
int bi = b.Length - 1;
for (int i = a.Length + b.Length - n; i >= 0; i--)
{
// find the lowset from a or b
if (ai >= 0)
{
if (bi >= 0)
{
if (a[ai] < b[bi])
{
result = a[ai];
ai--;
}
else
{
result = b[bi];
bi--;
}
}
else
{
result = a[ai];
ai--;
}
}
else
{
if (bi >= 0)
{
result = b[bi];
bi--;
}
else
{
// error, n is greater than a.Length + b.Length
}
}
}
}
Console.WriteLine("{0} th highest = {1}", n, result);
答案 4 :(得分:-1)
虽然是次线性的?你不能拥有一个不检查至少n个元素的算法,即使验证解决方案需要检查那么多。但是这里问题的大小肯定意味着数组的大小,所以只检查n个元素的算法是次线性的。
所以我觉得这里没有诀窍,从带有较小起始元素的列表开始直到你要么: