给定2个排序的整数数组,找到次线性时间中的第n个最大数

时间:2011-01-14 00:19:23

标签: arrays algorithm time-complexity

  

可能重复:
  How to find the kth smallest element in the union of two sorted arrays?

这是一个问题,我的一位朋友告诉我他在面试时被问到,我一直在考虑解决方案。

次线性时间对我来说意味着对数,所以也许是某种分而治之的方法。为简单起见,假设两个数组的大小相同,并且所有元素都是唯一的

5 个答案:

答案 0 :(得分:16)

我认为这是子阵列A[0..n-1]B[0..n-1]上的两个并发二进制搜索,即O(log n)。

  • 鉴于已排序的数组,您知道 nth 最大值将出现在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个元素的算法是次线性的。

所以我觉得这里没有诀窍,从带有较小起始元素的列表开始直到你要么:

  1. 到达第n个元素,你就完成了。
  2. 查找下一个元素大于另一个列表中的下一个元素,此时切换到另一个列表。
  3. 耗尽元素并切换。