演算法32 計算陣列中的逆序對

2022-07-05 16:00:11 字數 3240 閱讀 6386

設 a[1...n] 是乙個陣列,如果對於 i < j 有 a[i] > a[j], 則 a[i] 和 a[j] 構成一對逆序。給定乙個陣列,計算陣列中逆序對的個數。例如陣列 a = , 則 是逆序對,返回 3。

兩個 for 迴圈列舉所有的數對,如果是逆序對,則 count++,最終返回 count 即可。時間複雜度 o(n^2),**如下:

1 #include 2 #include 

3 #include 4

using

namespace

std;56

int countinversions(const vector&a)715

}16return

cnt;17}

1819

intmain()20;

22 vector v(a, a + 4

);23 cout << countinversions(v) <

24return0;

25 }

這個解法在《演算法導論》中歸併排序那一節的思考題 2-4 中有提到。原題如下:

設 a[1...n] 是一包含 n 個不同數的陣列,如果在 i < j 的情況下,有 a[i] > a[j], 則 (i, j) 稱為 a 中的乙個逆序(inversion)。

(a) 列舉出 <2, 3, 8, 6, 1> 的 5 個逆序對。      

答: <2, 1> <3, 1> <8, 6> <8, 1> <6, 1>

(b) 如果陣列的元素取自集合 , 那麼怎麼樣的陣列含有最多的逆序? 它包含多少個逆序?

答:n 個元素最多有 n(n-1) / 2 個陣列,在最壞的情況下,所有的數對都是逆序,因而最多有 n(n-1)/2 個逆序。這樣的陣列為倒序

(c) 插入排序的執行時間與輸入陣列中逆序對的數量之間有怎麼樣的關係?說明你的理由。

答:插入排序的核心過程如下,可以看到內迴圈的執行實際上是因為 構成了逆序,換句話說,內迴圈執行完畢後後 的逆序被消去,所以插入排序總的迴圈次數 == 陣列中逆序對個數。

1

for (int i = 1; i < n; ++i)

28 a[j + 1] =x;

9 }

歸併排序的基本思想就是 divide & conquer: 將陣列劃分為左右兩部分,歸併排序左部分,歸併排序右部分,然後 merge 左右兩個陣列為乙個新的陣列,從而完成排序。按照這個基本思想,我們也可以運用到計算逆序對中來,假設我們將陣列劃分左右兩部分,並且我告訴你左邊部分的逆序對有 inv1 個, 右邊部分的逆序對有 inv2 個,如下圖所示

那麼剩餘的逆序對必然是乙個數出現在左邊部分,乙個數出現在右邊部分,並且滿足出現左邊部分的數 a[i] > a[j], 如下圖所示, 值得說明是:左右兩部排序與否,不會影響這種情況下的逆序對數,因為左右兩部分的排序只是消除了兩部分內部的逆序對,而對於 a[i] 來自左邊, a[j] 來自右邊構成的逆序,各自排序後還是逆序

那麼接下來就需要我們就需要在 merge 的過程中計算 a[i], a[j] 分別來自左右兩部分的逆序對數,如下圖所示,如果所有 a[i] 都小於 b[j] 的第乙個數,顯然是沒有逆序的。只有當 a[i] > b[j] 是才會發生逆序,由於我們事先對 a 和 b 已經排好序,而所以如果發生 a[i] > b[j], 那麼所有 a[ii] (ii > i) 也都滿足 a[ii] > b[j], 也就是說和 b[j] 構成逆序的數有 ,所以逆序數增加 end- i + 1個, 所以我們每次碰到 a[i] > b[j] 的情況, 逆序對數增加 (end - i + 1) 個即可。

至此整個演算法的框架圖可以如下表示:

整個演算法的**如下,時間複雜度當然是 o(nlogn)

1 #include 2 #include 

3 #include 4

using

namespace

std;56

7 typedef long

long

llong;

89 llong mergeandcount(vectorint> &a, int left, int mid, int right, vectorint> &temp);

10 llong mergesortandcount(vectorint>& a, int left, int right, vectorint>&temp);

11 llong countinversions(vectorint>&a);

1213

intmain()

1425

26 llong result =countinversions(a);

27 cout << result <

28return0;

29}3031 llong mergeandcount(vectorint> &a, int left, int mid, int right, vectorint> &temp)

3244

else

4549}50

51while (i <= mid) temp[k++] = a[i++];

52while (j <= right) temp[k++] = a[j++];

5354

for (i = left; i <= right; ++i)

5558

return

cnt;59}

6061 llong mergesortandcount(vectorint>& a, int left, int right, vectorint>&temp)

6274

75 llong countinversions(vectorint>&a)

76

[1] 《演算法導論》中文第二版, p24,2-4 逆序對。

[2] 

[3]  www.cs.umd.edu/class/fall2009/cmsc451/lectures/lec08-inversions.pdf

演算法概論 分治演算法 計算陣列中的逆序對

題目1 在陣列中的兩個數字如果前面乙個數字大於後面的數字,則這兩個數字組成乙個逆序對。輸入乙個陣列,求出這個陣列中的逆序對的總數。例如在陣列中,共存在5組逆序對。最直觀的方法就是直接求解了,依次掃瞄陣列中的各個數字,並將其與其後的數字作比較。還有一種方法就是,利用分治的思想 比如陣列data 我們可...

歸併排序計算陣列中的逆序對

題目描述 在陣列中的兩個數字,如果前面乙個數字大於後面的數字,則這兩個數字組成乙個逆序對。輸入乙個陣列,求出這個陣列中的逆序對的總數p。並將p對1000000007取模的結果輸出。即輸出p 1000000007 輸入描述 題目保證輸入的陣列中沒有的相同的數字 資料範圍 對於 50的資料,size 1...

32 陣列中的逆序對

陣列中的逆序對 在陣列中的兩個數字,如果前面乙個數字大於後面的數字,則這兩個數字組成乙個逆序對。輸入乙個陣列,求出這個陣列中的逆序對的總數。沒想到這道題能暴力破解。class solution return retval 這道題我看的劍指offer上的 inversepairscore函式的形參和實...