逆序數問題的形化表示
輸入:一組n個不同的數的序列A[n]
輸入:逆序數對數記爲 num,如果 i < j 而 a[i] > a[j] ,那麼就是逆序數對
逆序數技術問題是排序算法的某種變形。
方法一:暴力破解法(類似於冒泡排序)
思路:列舉出所有的數組對,一共有 n*(n-1)/2 對,判斷這些是否爲逆序數對數。
時間複雜度:O(n^2)
方法二:運用divide-and-conquer,借鑑歸併排序或者快排的思想,其時間複雜度爲 O(nlogn)
1) 借鑑歸併排序思想
將一個規模爲n的逆序數計數問題分成2個規模爲n/2的子問題,遞歸調用計算各個子問題產生的逆序數記爲 RC_left 和 RC_right ,再將子問題進行合併同時計算此過程中產生的逆序數記爲 RC,最後返回的是已經排序好的序列以及A[n]中的逆序數對數 num = RC + RC_left + RC_right 。
如圖,假設左右兩個子序列已經排序好,其逆序數對數分別爲 RC_left 和 RC_right
在合併過程中5與4進行比較,可得出一對逆序數對。將4排序好後,就省去了與後續的 6,7,8進行比較,相對於暴力算法節省了次數。重複此過程完成兩個子問題的合併得到逆序數對數爲RC。
算法代碼如下
std::vector<int> v (100000) ;
std::vector<int> v_temp(100000) ;
int SortAndCount ( std::vector<int> & v , int low , int high ) {
if ( low >= high )
return 0;
int mid = ( low + high ) / 2 ;
int left_RC = SortAndCount ( v , low , mid ) ;
int Right_RC = SortAndCount ( v , mid+1, high ) ;
int RC = MergeAndCount ( v , low , mid , high ) ;
return ( RC + left_RC + Right_RC ) ;
}
int MergeAndCount ( std::vector<int> & v , int low , int mid , int high ) {
//i,j分別爲左右兩個序列的索引,k爲排序後的新數組的索引
int i = low , k = low , j = mid+1 , RC = 0 ;
while ( i <= mid && j <= high ) {
if ( v [i] < v [j] )
v_temp [k++] = v[i++] ;
else {
v_temp [k++] = v[j++] ;
RC += mid - i + 1 ;
}
}
while ( i <= mid ) v_temp[k++] = v[i++] ;
while( j <= high ) v_temp[k++] = v[j++] ;
//將排序後的數組元素賦值給v
for( int m = low ; m <= high ; m++ )
v[m] = v_temp[m] ;
return RC ;
}
2)運用divide-and-conquer方法,借鑑快排的思想
[據大神說,是可以用快排做出來的,以後來更]