歸並排序+歸並排序求逆序對(例題P1908)


歸並排序(merge sort)

顧名思義,這是一種排序算法,時間復雜度為O(nlogn),時間復雜度上和快排一樣

歸並排序是分治思想的應用,我們先將n個數不斷地二分,最后得到n個長度為1的區間,顯然,這n個小區間都是單調的,隨后合並相鄰的兩個區間,得到n/2個單增(減)的區間,隨后我們繼續合並相鄰的兩個區間,得到n/4個單增(減)的區間....

每次合並操作的總時間復雜度為O(n),logn次合並用時O(logn),故總時間復雜度為O(nlogn)

合並操作比較好理解,就像下圖這樣二分區間即可(紅線代表分割線):

 

然后,我們要如何實現O(n)的復雜度實現區間合並呢?

我們另開一個大小和原數組a大小一樣的數組alt,存儲需要合並的兩個區間的數,方便起見,我們用pos代表alt數組的當前指向的位置,用i表示左區間當前所指的位置,用j表示右區間當前所指的位置,如下圖所示:

記此時我們合並形成的區間為[l,r],按升序排序,那么我們枚舉這一區間中的pos,每次比較alt[i]和alt[j],如果alt[i] < alt[j] 那么令a[pos] = alt[i],同時pos++,i++ ,否則令a[pos] = alt[j] ,同時pos++,j++,如果左區間的數已經全部遍歷,那么將右區間剩下的數依次加入pos位置,反之同理,操作過程如下圖所示:

  

    

  

至此,區間[l,r]這一段區間已經完成排序,這就是歸並排序的合並過程

歸並排序代碼

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>

#define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 1e9 + 7;
const ll mod = 1e9 + 7;
const int Max = 5e5 + 10;

int n;
ll sum;
int alt[Max];

void merge(int a[], int l, int r)
{
    for(int i= l; i <= r; i ++)
    {
        alt[i] = a[i];
    }
    int mid = (l + r) >> 1;
    int i = l, j = mid + 1;
    for (int pos = l; pos <= r; pos++)
    {
        if (i == mid + 1)
        {
            a[pos] = alt[j];
            j++;
        }
        else if (j == r + 1)
        {
            a[pos] = alt[i];
            i++;
        }
        else if (alt[i] > alt[j])
        {
            a[pos] = alt[j];
            j++;
        }
        else
        {
            a[pos] = alt[i];
            i++;
        }
    }
}

void merge_sort(int a[], int l, int r)
{
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    merge_sort(a, l, mid);
    merge_sort(a, mid + 1, r);
    merge(a, l, r);
}


int a[Max];

int main()
{
#ifdef LOCAL
    //    freopen("input.txt", "r", stdin);
    //    freopen("output.txt", "w", stdout);
#endif
    sum = 0;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", a + i);
    merge_sort(a, 0, n - 1);
    for(int i = 0 ;i < n ;i ++)
        printf("%d%c",a[i],i == n-1?'\n':' ');
    return 0;
}
View Code

利用歸並排序求逆序對

我們注意到在歸並排序過程中,我們有一步判斷:if(alt[i] > alt[j]) ,如果判斷為真,那么顯然,j 和 區間[i,mid]每一個點都形成逆序對,一共mid-i+1個,而且只在這個地方會出現形成逆序對的情況,那么情況就很簡單了,我們將原數組進行歸並排序,並在if(alt[i] > alt[j] ) 為真的時候,統計一下逆序對的個數即可。

代碼區

(點擊此處查看模板題)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip>

#define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 1e9 + 7;
const ll mod = 1e9 + 7;
const int Max = 5e5 + 10;

int n;
ll sum;
int alt[Max];

void merge(int a[], int l, int r)
{
    for(int i= l; i <= r; i ++)
    {
        alt[i] = a[i];
    }
    int mid = (l + r) >> 1;
    int i = l, j = mid + 1;
    for (int pos = l; pos <= r; pos++)
    {
        if (i == mid + 1)
        {
            a[pos] = alt[j];
            j++;
        }
        else if (j == r + 1)
        {
            a[pos] = alt[i];
            i++;
        }
        else if (alt[i] > alt[j])
        {
            a[pos] = alt[j];
            j++;
            sum += mid - i + 1;        //i及其此后的都可以和a[j]形成逆序對
        }
        else
        {
            a[pos] = alt[i];
            i++;
        }
    }
}

void merge_sort(int a[], int l, int r)
{
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    merge_sort(a, l, mid);
    merge_sort(a, mid + 1, r);
    merge(a, l, r);
}


int a[Max];

int main()
{
#ifdef LOCAL
    //    freopen("input.txt", "r", stdin);
    //    freopen("output.txt", "w", stdout);
#endif
    sum = 0;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", a + i);
    merge_sort(a, 0, n - 1);
    printf("%lld\n", sum);
    return 0;
}
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM