自然合併排序

自然合併排序算法是對合並排序算法的一種改進。設 a[0:n-1]是無序數組,用一次對數組a的掃描可以找出其中 自然排好序的子數組,然後將相鄰的排好序的子數組段兩 兩合併,繼續合併相鄰排好序的子數組段,直至將整個數 組排好序。

code:

#include <algorithm>
#include <iostream>

using namespace std;

int getIndex(int *a, int *t, int n)
{
    // 記錄初始自然排好序的子數組段的起始下標及個數
    int cnt = 0;
    t[cnt++] = 0;	// 第一個下標爲0
    for(int i = 0; i < n-1; i++)
    {
        if(a[i+1] < a[i])
            t[cnt++] = i+1;
    }
    return cnt;
}

// 合併a中有序的兩段子數組到b對應的位置
// l是第一個有序子數組段的起始下標
// m是第一個有序子數組段的末尾下標
// r是第二個有序子數組段的末尾下標
void merge(int *a, int *b, int left, int mid, int right)
{
    int i = left;
    int j = mid+1;	// j爲第二個的起始下標
    int k = left;
    while(i <= mid && j <= right)	// 合併兩段
    {
        if(a[i] <= a[j])	// 每次取兩段中較小的一個
            b[k++] = a[i++];
        else
            b[k++] = a[j++];
    }
    // 合併兩段中的剩餘部分
    while(i <= mid) b[k++] = a[i++];
    while(j <= right)	b[k++] = a[j++];
}

void mergePass(int *a, int *b, int *t, int s, int n, int cnt)
{
    int i = 0;
    while(i <= cnt - 2*s)	// 用子段數組個數控制循環
    {
        if(i + 2*s < cnt)
            merge(a, b, t[i], t[i+s] - 1, t[i + 2*s] -1);
        else
            merge(a, b, t[i], t[i+s] - 1, n -1);
        i = i + 2*s;
    }
    // 剩餘未合併的不足s
    if(i + s < cnt)
        merge(a, b, t[i], t[i+s] - 1, n-1);
    else if(i < cnt)
        for(int j = t[i]; j <= n-1; j++)
            b[j] = a[j];
}

void mergeSort(int *a, int *t, int n, int cnt)
{
    // s指的是每次合併的子段數或者說步長
    // s = 1時是是對於初始分段1-1合併
    // s = 2時是相對與初始分段2-2合併,2是之前合併好的1-1
    int b[100];
    int s = 1;
    while(s < cnt)
    {
        mergePass(a, b, t, s, n, cnt);	// 合併數組到b
        s += s;
        mergePass(b, a, t, s, n, cnt);	// 合併數組回a
        s += s;
    }
}


int main()
{
    int n;
    int a[100], t[100];
    cin >> n;
    for(int i = 0; i < n; i++)
        cin >> a[i];
    int cnt = getIndex(a, t, n);
    mergeSort(a, t, n, cnt);
    for(int i = 0; i < n; i++)
        cout << a[i] << ' ';

    return 0;
}

 

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