分治算法之排序和逆序對

今天重新學習了一下分治算法,又溫習了一下快速排序和歸併排序的思路。

歸併排序

【思路】①遞歸到只剩一個元素
②將序列分成長度相等或接近的兩部分。
③遞歸——對劃分後的兩部分序列分別進行歸併排序。
④合併——遞歸地排序之後,兩邊的序列都已經有序,接下來需要將這兩個有序序列合併。
如何合併呢:我們可以建立一個隊列,然後在兩個序列的開頭設立一個指針,兩個指針指向的數值進行比較,把小的放進隊列,然後小的那個指針向後移一位。如果合併完之後其中一個指針還沒到序列的頭,那就把那個序列指針及後面的元素加入隊列,然後在重新賦給原來的序列即可。
接下來要說和歸併排序相關的一個知識,逆序對

逆序對

【問題描述】對於一個序列 a1, a2, a3,…, an, 如果存在 i<j, 使 ai>aj, 那麼 ai, aj就是一個逆序對。現在給你一個有 N 個元素的序列,請求出序列內逆序對的個數。 N≤100000。
【思路】歸併排序是將數列a[l,h]分成兩半a[l,mid]和a[mid+1,h]分別進行歸併排序,然後再將這兩半合併起來。
在合併的過程中(設l<=i<=mid,mid+1<=j<=h),當a[i]<=a[j]時,並不產生逆序數;
當a[i]>a[j]時,在前半部分中比a[i]大的數都比a[j]大,將a[j]放在a[i]前面的話,逆序數要加上mid+1-i。
因此,可以在歸併排序中的合併過程中計算逆序數.
【參考程序】

int cnt=0;//逆序對個數 
int a[10002],c[10002];//c臨時隊列 
void mergesort(int l,int r){
    int mid,j,i,tmp;
    if(r>l+1){
        int mid=(l+r)/2;
        mergesort(l,mid);
        mergesort(mid+1,r);
        tmp=l;
        for(i=l,j=mid;i<mid&&j<r;){
            if(a[i]>a[j]){
                c[tmp++]=a[j];
                cnt+=mid-i;
            }
            else c[tmp++]=a[i];
        } 
        if(j<r)for(;j<r;j++)c[tmp++]=a[j];
        else c[tmp++]=a[i];
        for(i=l;i<r;i++)a[i]=c[i];
    }
}

另外,還可以用樹狀數組來求逆序對,見我的博客

http://blog.csdn.net/qq_37416823/article/details/77622730

快速排序

快速排序的思路如下:如果序列中只有一個數字,則認爲已經排好序了。
在待排序序列中任取一個數(一般取第一個),作爲“標誌”。掃描序列中的其他數,將比“標誌”小的數
放到它的左面,將比“標誌”大的數放到它的右面。 完成後,“標誌”就把序列分成了兩部分,一部分的數都比
它小,另一部分的數都比它大。
接下來,對被劃分出的兩部分分別進行快速排序就可以了。(因爲遞歸結束後,序列已經有序,所以不需要
對結果進行合併了。)

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