[英]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,那么我們可以得出結論,答案為:
可以通過遞歸調用該函數來支持前兩個元素,但是如何計算第三個元素呢?
這個想法是從左到右遍歷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.