用O(nlogn)时间解决问题

时间:2010-09-30 23:59:10

标签: algorithm

设A [1]< = A [2]< = ....< = A [n]。设X是任意数。给出算法找到A [i]和A [j]的所有对,使得A [j] -A [i]> = X.所有数字都是正整数。

如果您想查看原始问题。这是:

设P = {p1; P2; ; pn}是二维空间中的一组n个点,其中对于每个i,pi =(xi; yi)。设D =(dx; dy)。问题是决定是否存在一对点pi和pj,使得xj-xi> = dx和yj-yi> = dy。通过考虑所有可能的点对,您可以在O(n ^ 2)时间内轻松解决此问题。但我们有兴趣开发一种O(n log n)时间算法。

8 个答案:

答案 0 :(得分:2)

在这里,您可以利用输入已排序且所有数字均为正整数的事实。如果

A[j] - A[i] ≥ X

然后我们知道以下也是如此

A[j + 1] - A[i] ≥ X

所以算法可能是

for(i = 1; i < n; i++) // for every value (this part is O(n))
{
    int minJ = A[i] + X; // the minimum J that satisfies `A[j] - A[i] >= X`

    int cutoff = binarySearch(minJ); // figure out the minimum J for which  `A[j] - A[i] >= X` (this part is O(log(n))

    storeResults(i, cutoff, n); // In Answers[i], save every qualifying integer (this part is less than O(log(n))
}

总的来说,你有

O(n *(log(n)+ less-than-log(n))
O(n *(2 log(n)))
O(n * log(n))

可以进行一些小的优化,比如只进行n - 1而不是n的主循环,但这对Big-Oh来说并不是非常关键或相关。

答案 1 :(得分:1)

显然,当max(A[j] - A[i])最大且A[j]最小时A[i]成就A[n] - A[1]i = n。因此,您只需要检查j = 1O(1)(i, j):)

修改
如果您需要查找所有此类对O(n^2),那么它显然是O(n^2)任务:因为在一般情况下会有for (int i = 0; i < n; ++i) { for (int j = 0; j < n && A[i] - A[j] >= X; ++j) { if (i != j) { print("new pair: ", i, j); } } } 个解决方案。所以,只需检查所有对。

{{1}}

答案 2 :(得分:0)

由于输入已排序,您可以从1到n为每个X - A[i]二进制搜索i。由于项目可以相等,因此您需要找到二进制搜索的上限和下限。我想这将是O(nlogn)......

答案 3 :(得分:0)

我很困惑为什么这不是一个O(n)问题:只需用索引i循环遍历值,让另一个索引j滞后,这样它总是在或之前列表中较早的值,其值至少比当前X A[i]A[i]。对于每个值A[j],请回顾X并查看差异是否正好i,j,如果是,请记住j对。然后提前A[j+1],如果需要,XA[i]的{​​{1}}范围内,并返回到循环的顶部。

编辑:Nikita Rybak非常正确,我错过了问题中的&gt; =但这只会使问题变得非常简单:代替给定的ji是一个有停止和开始的范围,我们只需要一个边界,因为例如A[6] - A[4] >= X,那么A[6]显然同样适用于A[3] {1}}下来。

答案 4 :(得分:0)

具有良好的数据结构,可以在O(n)

中完成

将A放入链接列表中。

创建另一个链接列表,引用A [i]和A [j]。

现在我们可以开始了。

Ai = Aj = first element of A
while Aj <> null
    if Aj - Ai < X 
    then 
        Aj = Aj.next
    else
        S.ai = Ai
        S.aj = Aj
        Ai = Ai.next
    end 
end

根据X,有一个循环,Aj将在50%到100%的案例之间前进。 由于链接列表,我们避免了副本爆炸。

==&GT; O(N)

随着2D问题的原始问题的增加,一些修改是有序的,索引必须与值一起存储。

有2个列表,其中包含元素xi - &gt; xk和yi - &gt; yl(其中xk和yl是最小的数字&gt; = xi + dx resp yi + dy。

如果你找到一对xi,yi,其中k-l与前一个i有不同的符号,你可以走xj或yj列表找到一个满足约束的点。

答案 5 :(得分:0)

你想找到一对数字A [j] - A [i]&gt; = X。

这应该可以在大约2nlogn时间内完成:

对于每个号码(订单n)
进行二分搜索(logn)以找到符合条件A [j] - A [i]的最后一个数字 打印出之前的所有数字(n)

在伪代码中:

for(int i = n; i > 0; i--) // O(n)
{
   int last_number = binarySearch(i); // O(log (n/2)) because on avg we only need to search half the list
   if(last_number != SOME_INVALID_THING)
   {
      for(int x = 0; x < last_number; x++) // O(n)
      {
         printf("%d %d", i, x);
      }
   }
}

这是对的吗?或者这只是n ^ 2,看起来你永远不会做很多工作,但嵌套循环意味着n ^ 2.

答案 6 :(得分:0)

我想我已经得到了它,但由于这有点像家庭作业,我只是给一个提示。我的算法使用一个点列表,这些点在任何时候都是有序的,因此x增加而y减小。积分被认为是一次一个,每个点落在四个案例中的一个:

  1. 找到满足条件的一对点。
  2. 我们可以证明,在没有错误地宣称没有答案的情况下忽略新观点是安全的。
  3. 新点插入到列表中,保留了有关其排序的不变量。
  4. 新点取代之前在列表中的一个或多个点,我们可以证明可以安全地忽略被删除的点。
  5. 如果以这种方式检查所有点而没有击中情况1,则输入中没有一对点满足条件。

答案 7 :(得分:0)

也许我在打破这个问题上非常糟糕,但我现在得到了原版的答案。这是我的解决方案:

步骤1:用x坐标对集合P进行排序。

步骤2:设Q = {q1,q2,...,q_n},其中qi = min {y1,y2,..,y_i}

步骤3:如果(x_n - x_midpoint&gt; = dx)

      if(y_n - q_midpoint >= dy)

        return there exit;

      else repeat step3 for x_n and the new midpoint of the range after the midpoint

   else

       repeat step 3 for x_n and the new midpoint of the range from the midpoint to the left.

       When there is only one element in the range and the pair hasn't been found, repeat Step 3 for x_n-1, x_n-2,..., x2. 

       If the pair hasn't been found after this, then there exist no such pair.

对不起所有人,如果我的第一个问题没有意义!