计算两个数组中的反转

时间:2014-12-23 13:23:19

标签: c++ arrays algorithm

阵列中的反转是一对索引(i,j),使得a [i]> a [j]且i <1。学家

给定2个阵列A和B,并且我们必须返回这样的对的数量,使得a [i]> b [j]且i <1。学家

示例:

令n = 3且A [] = [5,6,7]且B [] = [1,2,3]则答案为3. 3对为(5,2),(5,3) (6,3)。

我的代码:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int len;
    scanf("%d",&len);
    int a[len];
    int b[len];
    for(int i = 0; i < len; i++)
       scanf("%d",&a[i]);
    for(int i = 0; i < len; i++)
       scanf("%d",&b[i]);
    int count = 0;
    for (int i = 0;i < len; i++)
    {
        for(int j = i+1; j < len; j++)
        {
             if(a[i] > b[j])
             {
                 count++;
             }
         }
     }
     printf("%d",count);
}

但这是O(N ^ 2)解决方案。我需要一个更好的解决方案,因为N <= 200000.I我知道我们可以在O(N * Log N)时间内计算相同数组中的反演。但是如何做到这一点对于两个不同的阵列?

3 个答案:

答案 0 :(得分:6)

written in the past关于如何使用Fenwick tree计算反转,这是一种非常有效的二叉树类型,可让您计算序列上的前缀聚合。

以下是针对您的方案的特别修改:

long long inversions(const vector<int>& a, const vector<int>& b) {
  int n = a.size();
  vector<int> values(a);
  for (int x: b) values.push_back(x);
  sort(begin(values), end(values));
  vector<int> counts(2*n + 1);
  long long res = 0;
  for (int i = n - 1; i >= 0; --i) {
    // compute sum of prefix 1..rank(a[i]) - 1
    for (int v = lower_bound(begin(values), end(values), a[i]) - begin(values);
         v; 
         v -= v & -v)
      res += counts[v];
    //add 1 to point rank(b[i])
    for (int v = lower_bound(begin(values), end(values), b[i]) - begin(values) + 1;
         v <= 2*n;
         v += v & -v)
      counts[v]++;
  }
  return res;
}

基本上我们从右到左遍历数组,维护一个数据结构,代表我们在后缀中看到的值。对于每个元素b [i],我们在最终结果中添加x&lt; = b [i] -1的数据结构中元素x的数量。然后我们将[i]添加到数据结构中。

数组values用于将值范围压缩为1..2n,因为Fenwick树在范围大小中采用线性空间。我们可以通过选择更全面的数据结构来避免这一步骤,例如具有子树大小增强的平衡bjnary搜索树。

复杂度为O(n log n),常数因子非常低。

答案 1 :(得分:0)

您可以使用合并排序的想法找到两个数组之间的求逆!

考虑到您有两个相同大小的数组,我们称它们为A,B 如果我们分别表示B的A,A1的前半部分和A,A2以及B1和B2的后半部分,那么我们可以得出结论,答案为:

  1. A1和B1之间的反转
  2. A2和B2之间的反转
  3. A1和B2之间的反转

可以通过递归调用该函数来支持前两个元素,但是如何计算第三个元素?

这个想法是要从左到右穿过A1和B2, 如果B1中的元素大于A1中的元素,则应该将尚未访问的A1中的元素相加,求反,最后我们仅将A1和A2排序为A,将B1以及B2排序为B

这是python中的代码:

def find_inv(A, B):

if len(A) <= 1:
    return 0
mid = len(A) // 2
A1 = A[:mid]
A2 = A[mid:]
B1 = B[:mid]
B2 = B[mid:]

if len(A1) >= 1 and len(B2) >= 1:
    ans = find_inv(A1, B1) + find_inv(A2, B2)
else:
    A.sort()
    B.sort()
    ans = 0
len_A = len(A1)
index_A = 0
len_B = len(B2)
index_B = 0

for k in range(len_A + len_B):

    if A1[index_A] <= B2[index_B]:

        index_A += 1
        if index_A == len_A:
            merge(A1, A2, A)
            merge(B1, B2, B)

            return ans
    else:
        index_B += 1
        ans += (len_A - index_A)

        if index_B == len_B:
            merge(A1, A2, A)
            merge(B1, B2, B)
            return ans

def merge(A1, A2, dest):
i = 0
j = 0


while i < len(A1) and j < len(A2):
    if A1[i] < A2[j]:
        dest[i+j] = A1[i]
        i += 1
    else:
        dest[i+j] = A2[j]
        j += 1
while i < len(A1):
        dest[i+j] = A1[i]
        i += 1

while j < len(A2):
        dest[i+j] = A2[j]
        j += 1

答案 2 :(得分:-1)

一个想法:
1.合并两个原始数组,使得具有相同索引的每对元素彼此相邻。 (您需要放置元素,使得较低元素位于较高元素之前) 3.计算结果数组中的反转次数,如下所述。

编辑:对不起,我误解了这个问题。如果您只想从(I)到b(j)进行反转,则可以使用另一个字段数组类型(a或b)标记每个元素。然后在mergesorting的同时,只有当反转是从arraytype a到b时,才能增加计数。