問題:給定一個數組A,A存有n個互不相同的整數。定義:若i<j且A[i]>A[j],則稱(i,j)為A的一個逆序對(inversation)。設計一個O(nlogn)算法求A中逆序對個數。
顯然最壞情況下逆序對有n(n-1)/2個,如;5 4 3 2 1完全逆序,逆序對有(5-1)*5/2=10對。若用暴力來求解,則時間復雜度為O(n2),顯然比這不是一個好的算法。下面考慮用歸並排序的類似方法來解決這個問題。
首先,對於一個長度為n的數組A[0...n-1],我們可以將它分為兩個長度為n/2子數組L[0...n1]=A[0...n/2]和R[0...n2]=A[n/2+1...n-1],總的逆序對=L中的逆序對+R中的逆序對+橫掃L和R的逆序對;
比如將數組 2 3 8 6 1分為兩部分 L: 2 3 8和 R:6 1,於是左邊L中逆序對0個,右邊R中逆序對(6,1)共1對,那么橫掃L與R的有(2,1),(3,1),(8,6),(8,1)一共4對,於是總的對數為0+1+4=5對。現在問題的難點就是如何求中間橫掃那部分的逆序對,如果暴力來求,必然O(n2)。我們先來看看這樣的一個事實,如果將L排好序,將R也排好序,這時候中間橫掃那部分的逆序對個數不會改變,這是因為L與R是分開的。這樣以來,我們可以用歸並排序的思路設計這個算法,基於這樣設計之所以正確在於每一次子序列的排序的歸並都是在該該序列逆序對計算完成后進行的。C++代碼如下:
#include<iostream> using namespace std; #define MAX_VALUE 12343564 int Count_Reversations(int *A, int n); int Merge_Reversations(int *A, int p, int r); int main(){ int a[5] = { 2,3,8,6,1 }; cout << Count_Reversations(a, 5) << endl; return 0; } int Count_Reversations(int*A, int n){ //接口函數 return Merge_Reversations(A, 0, n - 1); } int Merge_Reversations(int *A, int p, int r){ if (p>=r) return 0; int i, j, k,mid,n1,n2, reversations = 0; mid = (p + r) >> 1; int next_count= Merge_Reversations(A, p, mid) + Merge_Reversations(A, mid + 1, r); n1 = mid - p + 1; n2 = r - mid; int *R = new int[n2 + 1]; int *L = new int[n1 + 1]; for (i = 0; i < n1; i++) L[i] = A[i + p]; for (j = 0; j < n2; j++) R[j] = A[j + mid + 1]; L[n1] = R[n2] = MAX_VALUE; //設置為最大值,當做哨兵 i = j = 0; for (k = p; k <= r; k++){ if (L[i]>R[j]){ reversations +=n1-i; A[k] = R[j++]; } else A[k] = L[i++]; } delete[]R, L; return reversations +next_count; }