歸併排序+逆序對

第二次接觸MergeSort與Inversions(逆序對),這次弄懂了每一個細節,很好的理解了兩者之間關係。下面總結一下相關技巧。

1.MergeSort和quickSort類似,主要採用了Divide-and-Conquer的思想,也就是將原本規模爲1的問題,二分爲規模相近的兩個獨立子問題,同時求解。因此這裏遞歸通常是常用技巧。這裏難點應該在邊界條件的判斷上。(當然這也是算法通有難點)。

   既然提到了quickSort,這裏不得不對比一下兩種排序算法的區別。時間複雜度上,MergeSort經歷了一個先分後合的過程,而quickSort在劃分成子問題時就已經在排序了,因此分好即排序完畢。因此,雖然兩者時間複雜度理論上都小於O(nlogn),但是快排好很多,尤其是快排的軸點選取法採用“任意三點取中法”時。空間複雜度上,前者在每次合併時至少需要開闢(lo+hi)>>1大小的數組備份前半部分數據,空間複雜度較大(可以結合後面的代碼理解);而後者,只需在每次劃分爲子問題時,另開闢一個相關類型的空間存儲軸點pivot。綜上,可以看出,快排好很多。

2.Inversions。逆序對指序列中與理想次序不一致的點對數。其可以定量衡量InsertionSort的效率。比如升序序列中,如果秩i<j,但是i對應的值卻大於j,則i和j對應的值構成一對逆序對。降序,相反。注意,這裏採用前向統計方法。比如序列(5,6,2,2,3),元素3對應的逆序對爲2,分別爲(5,3),(6,3).這裏是關鍵

3.在MergeSort中如何統計Inversions?在相鄰序列歸併的過程過程中,如果一旦存在後序元素大於前序,則存在逆序對,且逆序對數恰好爲(lb-i)。其中,i爲前序元素的秩,lb爲前序元素的大小。請自行畫圖並結合一下代碼理解。

完整代碼如下:

#include <iostream>
#include <vector>

using namespace std;

vector<int*> sequence;
int cnt = 0;                    //統計逆序對數

void merge(int lo, int mid, int hi){
    int lb = mid - lo;
    int s = lo;

    vector<int*> B(lb);
    for (int i = 0; i < lb; i++){    //深拷貝
        int* a = new int;
        *a = *sequence[s++];
        B[i] = a;
    }


    //開始比較操作
    int i = 0;

    //一共有四種邊界情況
    while (hi>lo)                                     
    {
        if ((i < lb) && (mid < hi)){
            if (*B[i] <= *sequence[mid])
                *sequence[lo++] = *B[i++];
            else{
                cnt += (lb - i);                         //統計逆序對
                *sequence[lo++] = *sequence[mid++];
            }

        }
        if ((i < lb) && !(mid < hi)){
            *sequence[lo++] = *B[i++];
            //cnt += (lb - i);                           //想一下這裏爲什麼要去掉
        }
        if (!(i<lb) && (mid < hi))
            *sequence[lo++] = *sequence[mid++];
    }
}

void mergeSort(int lo, int hi){
    if (hi - lo < 2)
        return;    //相鄰元素自然有序
    int mid = (lo + hi) >> 1;
    mergeSort(lo, mid);
    mergeSort(mid, hi);
    merge(lo, mid, hi);
}

int main(){
    int n;
    scanf("%d", &n);
    sequence.resize(n);

    for (int i = 0; i < n; i++)
    {
        int* a = new int;
        scanf("%d", a);
        sequence[i] = a;
    }

    mergeSort(0, sequence.size());
    for (int i = 0; i < n; i++)
    {
        cout << *sequence[i] << " ";
    }
    cout << endl << cnt << endl;

    return 0;
}

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