计算排列中的“反转”数

时间:2011-06-29 16:05:26

标签: arrays algorithm complexity-theory

设A是一个大小为N的数组。 如果(i,j)i < j

,我们会将几个索引A[i] > A[j]称为“反向”

我需要找到一个接收大小为N的数组(带有唯一数字)的算法,并返回O(n*log(n))时间内的倒数。

4 个答案:

答案 0 :(得分:25)

您可以使用merge sort算法。

在合并算法的循环中,左半部分和右半部分都是按升序排序的,我们希望将它们合并为一个排序的数组。请注意,右侧的所有元素都具有比左侧更高的索引。

假设 array [leftIndex]&gt;阵列[rightIndex] 即可。这意味着索引 leftIndex 元素后左侧部分中的所有元素也大于右侧当前元素(因为左侧按升序排序)。因此,右侧的当前元素会生成 numberOfElementsInTheLeftSide - leftIndex + 1 反转,因此请将其添加到全局反转计数中。

一旦算法完成执行,你得到答案,并且在最坏的情况下合并排序是 O(n log n)

答案 1 :(得分:12)

Cham和Patrascu于2010年在SIAM上发表了一篇名为Counting Inversions, Offline Orthogonal Range Counting, and Related Problems的文章,该文章给出了一个采用O(n sqrt(log(n)))时间的算法。这是目前最着名的算法,并且改进了长期存在的O(n log(n)/ log(log(n)))算法。从摘要:

  

我们提供 O(n sqrt(lg n)) -time算法   用于计算反转次数   在n个元素的排列中。这个   改善了以前的长期存在    O(n lg n / lg lg n)的界限   接着来自Dietz的数据结构   [WADS'89],并回答了一个问题   安德森和彼得森[SODA'95]。如   Dietz的结果被认为是最佳的   对于相关的动态排名问题,   我们的结果显示了重大意义   离线设置的改进。

     

我们的新技术非常简单:我们   执行a的“垂直分区”   特里(类似于范埃姆德博阿斯树),   并使用来自外部记忆的想法。   然而,该技术发现了许多   应用程序:例如,我们获取

     
      
  • d 维度中,算法为   回答 n 离线正交范围   及时计算查询 O(n   lg d-2 + 1 / d n);
  •   
  • 一个改进   在线数据的建设时间   正交范围的结构   数数;
  •   
  • 改进了更新时间   对于部分和的问题;
  •   
  • 更快   用于查找的Word RAM算法   安排的最大深度   轴对齐的矩形,并用于   坡度选择问题。
  •   
     

作为奖励,   我们也给出一个简单的   (1 +ε) - 近似算法   计算运行中的反转   线性时间,改善以前    O(n lg lg n)受Andersson和   皮特森。

答案 2 :(得分:10)

我认为最好的方法(这只是因为我喜欢数据结构)是使用binary indexed tree。请注意,如果你需要的只是一个解决方案,合并排序也会起作用(我只是认为这个概念完全是摇滚!)。基本思想是这样的:构建一个更新O(log n)中的值的数据结构,并回答查询“到目前为止,数组中已经发生了多少小于x的数字?”鉴于此,您可以轻松回答有多少大于x,这有助于反转,x作为对中的第二个数字。例如,考虑列表{3,4,1,2}。

当处理3时,到目前为止没有其他数字,因此右侧3的反转= 0 当处理4时,到目前为止小于4的数字= 1,因此数字越多(因此倒数)= 0 现在,当处理1,小于1 = 0的数字时,这个更大的数字= 2,这有助于两个反转(3,1)和(4,1)。同样的逻辑适用于2,它找到比它小1的数字,因此比它大2。

现在,唯一的问题是要了解这些更新和查询如何在log n中发生。上面提到的url是我读过的关于这个主题的最好的教程之一。

答案 3 :(得分:0)

这些是原始的MERGE和MERGE-SORT算法 来自Cormen,Leiserson,Rivest,Stein算法简介:

MERGE(A,p,q,r)
 1  n1 = q - p + 1
 2  n2 = r - q
 3  let L[1..n1 + 1] and R[1..n2 + 1] be new arrays
 4  for i = 1 to n1
 5      L[i] = A[p + i - 1]
 6  for j = 1 to n2
 7      R[j] = A[q + j]
 8  L[n1 + 1] = infinity
 9  R[n2 + 1] = infinity
10  i = 1
11  j = 1
12  for k = p to r
13      if L[i] <= R[j] 
14          A[k] = L[i]
15          i = i + 1
16      else A[k] = R[j]
17          j = j + 1

MERGE-SORT(A,p,r)
 1 if p < r
 2     q = floor((p + r)/2)
 3     MERGE-SORT(A,p,q)
 4     MERGE-SORT(A,q + 1,r)
 5     MERGE(A,p,q,r)
在MERGE infinity的第8和第9行是所谓的前哨卡, 它具有所有数组元素都小于它的值。 要获得反转次数,可以引入全局计数器, 让我们说ninv在调用MERGE-SORT之前初始化为零 而不是通过添加一行来修改MERGE算法 在第16行之后的else语句中,类似

ninv += n1 - i

比MERGE-SORT完成后,ninv将保持反转次数