簡體   English   中英

計算兩個數組中的反轉

[英]Count inversions in two arrays

數組中的求逆是一對索引(i,j),使得a [i]> a [j]和i <j。

給定2個數組A和B,我們必須返回這樣的對數,即a [i]> b [j]和i <j。

范例:

令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。我知道我們可以在O(N * Log N)的時間內對同一數組中的求反進行計數,但是如何針對兩種不同的方法完成數組?

過去,我曾寫過關於如何使用Fenwick樹進行反轉計數的方法, Fenwick樹是一種非常有效的二叉樹類型,可讓您計算序列上的前綴聚合。

這是針對您的方案的即席修改:

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;
}

基本上,我們從右到左遍歷數組,維護一個數據結構,該結構表示我們在后綴中已經看到的a的值。 對於每個元素b [i],我們將x <= b [i]-1的數據結構中元素x的數量添加到最終結果中。然后將a [i]添加到數據結構中。

數組values用於將values的范圍壓縮為1..2n,因為Fenwick樹在范圍大小上采用線性空間。 我們可以通過選擇功能更全的數據結構(例如帶有子樹大小增加的平衡bjnary搜索樹)來避免該步驟。

復雜度為O(n log n),常數因數非常低。

您可以使用合並排序的想法找到兩個數組之間的求反!

考慮到您有兩個相同大小的數組,如果我們分別為B表示A,A1的前半部分和A,A2以及B1和B2的后半部分,則稱它們為A,B,那么我們可以得出結論,答案為:

  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

一個想法:
1.合並兩個原始數組,以使具有相同索引的每一對元素彼此相鄰。 (您將需要放置元素,使較低的元素位於較高的元素之前)。
3.如下所述,計算結果數組中的反轉次數。

編輯:對不起,我誤解了這個問題。 如果只想將a(I)轉換為b(j),則可以將每個元素標記為另一個字段arraytype(a或b)。 然后,在進行合並排序時,僅當反轉是從數組類型a到b時,才可以增加計數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM