1. 程式人生 > 實用技巧 >歸併排序及其應用(逆序對數,相鄰兩數兩兩交換最少交換次數)

歸併排序及其應用(逆序對數,相鄰兩數兩兩交換最少交換次數)

歸併排序

所謂歸併排序是指將兩個或兩個以上有序的數列(或有序表),合併成一個仍然有序的數列(或有序表)。這樣的排序方法經常用於多個有序的資料檔案歸併成一個有序的資料檔案。歸併排序的演算法比較簡單。

1. 基本思想

歸併排序是用分治思想,分治模式在每一層遞迴上有三個步驟:

  • 分解(Divide):將n個元素分成2個含n/2個元素的子序列(n為奇數時,有一個序列會多一)。
  • 解決(Conquer):用合併排序法對兩個子序列遞迴的排序。
  • 合併(Combine):合併兩個已排序的子序列已得到排序結果。

2.實現邏輯

2.1工作原理

① 申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合併後的序列

② 設定兩個指標,最初位置分別為兩個已經排序序列的起始位置
③ 比較兩個指標所指向的元素,選擇相對小的元素放入到合併空間,並移動指標到下一位置
④ 重複步驟③直到某一指標到達序列尾
⑤ 將另一序列剩下的所有元素直接複製到合併序列尾

2.2 遞迴實現

① 將序列每相鄰兩個數字進行歸併操作,形成floor(n/2)個序列,排序後每個序列包含兩個元素
② 將上述序列再次歸併,形成floor(n/4)個序列,每個序列包含四個元素
③ 重複步驟②,直到所有元素排序完畢

4. 複雜度分析

平均時間複雜度:O(nlogn)
最佳時間複雜度:O(n)
最差時間複雜度:O(nlogn)
空間複雜度:O(n)
排序方式:In-place

穩定性:穩定

#include<cstdio>
const int maxn=500010;
int a[maxn],c[maxn];
int n;
long long ans=0;
void msort(int l,int r){
    if(l==r) return ;
    int mid=(l+r)/2;
    msort(l,mid);//分解 
    msort(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r){//合併 
        if(a[i]>a[j]) {
        
// ans+=mid-i+1; c[k++]=a[j++]; } else c[k++]=a[i++]; } while(i<=mid) c[k++]=a[i++]; while(j<=r) c[k++]=a[j++]; for(int i=l;i<=r;i++) a[i]=c[i]; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); msort(1,n); for(int i=1;i<=n;i++) printf("%d",a[i]); //printf("%lld\n",ans);//逆序對數 return 0; }
View Code

歸併的空間複雜度就是那個臨時的陣列和遞迴時壓入棧的資料佔用的空間:n + logn;所以空間複雜度為: O(n)。

歸併排序演算法中,歸併最後到底都是相鄰元素之間的比較交換,並不會發生相同元素的相對位置發生變化,故是穩定性演算法。

歸併排序應用

問題1:求通過相鄰位置的數字交換,使數列有序的最少交換次數?

問題2:求數列中的逆序對數。

這兩個問題實際上是一個問題。

歸併排序的交換次數就是這個數列的逆序對個數(也是使數列有序的相鄰兩元素交換的最少交換次數),為什麼呢?

歸併排序是將數列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,即逆序數要加上mid+1-i。

因此,可以在歸併排序中的合併過程中計算逆序數,也即求出了相鄰兩數交換的最少次數。

比如

i=1 mid=2 j=3

3 8 | 2 6

右邊的2如果想放到3的前面,需要分別和8交換,再和3 交換,交換次數為2,即逆序對數為2=mid-i+1

A數列:3 2 1 4 B數列:2 3 1 4

下標: 1 2 3 4 下標: 1 2 3 4

A排序後:1 2 3 4 B排序後: 1 2 3 4

對應下標:3 2 1 4 對應下標:3 1 2 4

排序後如果A和B對應的下標相等,則結束,如果不相等則需要交換。

C[Ai下標]=Bi下標,

即如果c[i]=i,則什麼也不用做,否則對c排序,使c[i]=i,求逆序對數即可

題目應用:洛谷:P1309,P1908,P1966