1. 程式人生 > 其它 >【無聊の瞎搞】倍增歸併

【無聊の瞎搞】倍增歸併

我不知道是否有人之前搞過這種東西

P1177 【模板】快速排序

就是一個...倍增寫的歸併排序,好像並沒有必要

時間複雜度 \(O(n \log_2 n)\),這裡的 \(n=2^k\)

如果一個序列長度為 \(m\),則 \(n\) 為最小的 \(2^k \ge m\)

基本思路:
好像沒什麼可以講的

實現:
1、用 \(- \infty\) 把序列湊成一個長度 \(2\) 的冪的序列,然後設定步長 \(step = 1\)
2、設指向區間起始點的 \(i = 1\)
3、合併 \(2\) 個序列,第一個為 \(i\) ~ \(i + \dfrac{step}{2} - 1\),第二個為 \(i + \dfrac{step}{2}\)

~ \(i + step - 1\),存到新陣列
4、重複執行 \(2\) 步,每次 \(i=i+step\),直到 \(i > m\)
5、賦值回原序列
6、重複執行 \(2\) 步,每次 \(step=2step\),直到 \(step > m\)
7、輸出除 \(- \infty\) 的序列

把序列湊成一個長度 \(2\) 的冪的序列是為了方便計算,也好寫

Code:

/*Copyright (C) 2013-2022 LZE*/
#include<bits/stdc++.h>
#define INF 0x7fffffff
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int N = 1000010;
ll T,n,m;
ll a[N] = {0},b[N] = {0};
int main(){
    scanf("%lld",&n);
    for(ll i = 1;i <= n;i++)scanf("%lld",&a[i]);
    ll p,lp,rp,limit = 1;
    for(ll i = 1;limit < n;i++)limit = limit * 2;
    ll bot = limit - n + 1;
    for(ll i = n + 1;i <= limit;i++)a[i] = -INF;
    for(ll step = 1;step <= limit;step = step * 2){
        for(ll i = 1;i <= limit;i = i + step){
            p = 1,lp = i,rp = i + step / 2;
            for(ll j = 1;j <= step;j++,p++){
                if(lp == i + step / 2){ b[p] = a[rp]; rp++; }
                else if(rp == i + step){ b[p] = a[lp]; lp++; }
                else if(a[lp] <= a[rp]){ b[p] = a[lp]; lp++; }
                else if(a[lp] > a[rp]){ b[p] = a[rp]; rp++; }
            }
            for(ll j = i,k = 1;k < p;j++,k++)a[j] = b[k];
        }
    }
    for(ll i = bot;i < n + bot;i++)printf("%lld ",a[i]);
    printf("\n");
    return 0;
}