逆序數計數問題

逆序數問題的形化表示
輸入:一組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方法,借鑑快排的思想
[據大神說,是可以用快排做出來的,以後來更]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章