要做算法題作業,關於自然合併排序的,不太清楚自然合併排序和歸併排序有什麼區別,在網上查找到下面的資料,很詳細,趕緊貼在下面和大家分享~~~
思想:將待排序元素分成大小大致相同的兩個子集,分別對兩個子集合進行排序,最終將排好序的子集合併成所要求的排好序的集合。T(n)最好情況=O(nlogn) T(n)最壞情況=O(nlogn) S(n)=O(n)
程序實例1:
#include<iostream>
using namespace std;
//合併數組中的兩端有序序列到一個新的數組,並複製回來排好序的結果到原數組
static void merge(int array[], int p, int q, int r)
{
int i,k;
int begin1,end1,begin2,end2;
int* temp = new int [r-p+1]; //申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列
begin1= p; end1 = q; //設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置
begin2 = q+1; end2 = r;
k = 0;
//比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置
while((begin1 <= end1)&&( begin2 <= end2))
{
if(array[begin1]<array[begin2])
{
temp[k] = array[begin1]; begin1++;
}
else
{
temp[k] = array[begin2]; begin2++;
}
k++;
}
while(begin1<=end1) //若第一個序列有剩餘,直接拷貝出來粘到合併序列尾
{
temp[k++] = array[begin1++];
}
while(begin2<=end2) //若第二個序列有剩餘,直接拷貝出來粘到合併序列尾
{
temp[k++] = array[begin2++];
}
for (i = 0; i < (r - p +1); i++) //將排序好的序列拷貝回數組中
array[p+i] = temp[i];
delete[] (temp);
}
//歸併算法的核心代碼
void merge_sort(int array[], unsigned int first, unsigned int last)
{
int mid = 0;
if(first<last)
{
mid = (first+last)/2;
merge_sort(array, first, mid);
merge_sort(array, mid+1,last);
merge(array,first,mid,last);
}
}
int main()
{
int a[]={2,26,45,76,97,36,26,87,4,3,1,5,9,8,6,7,10,65,43,85};
merge_sort(a, 0, 19);
for(int i=0;i<20;i++)
cout<<a[i]<<endl;
system("pause");
return 0;
}
歸併排序可以改進,依次兩兩合併,四四合並,…………,直到合併結束。
程序實例2:(改進的歸併排序)
#include<iostream>
using namespace std;
/**********************************************************
算法說明:
本算法是歸併算法的一種改進,省去了遞歸的過程
***********************************************************/
//將c數組中的l~m,m~r段合併到d數組從l到r對應位置
void Merge(int c[],int d[],int l,int m, int r)
{
int i=l,j=m+1,k=l; //這裏都是l
while((i<=m)&&(j<=r))
if(c[i]<=c[j]) d[k++]=c[i++];
else d[k++]=c[j++];
if(i>m)for(int q=j;q<=r;q++)d[k++]=c[q];
else for(int q=i;q<=m;q++)d[k++]=c[q];
}
//對於不同的長度s,歸併趟算法如下
void MergePass(int x[],int y[],int s,int n)
{
int i=0;
while(i<=n-2*s)
{
Merge(x,y,i,i+s-1,i+2*s-1);
i=i+2*s;
}
if(i+s<n)
Merge(x,y,i,i+s-1,n-1);
else
for(int j=i;j<=n-1;j++)
y[j]=x[j];
}
//歸併的主算法
void Mergesort(int a[],int n)
{
int *b=new int[n];
int s=1;
while(s<n)
{
MergePass(a,b,s,n);
s+=s;
MergePass(b,a,s,n); // 由於MergePass存在一種賦值的情況保證最終的a是變換後的,即排好序的
s+=s;
}
delete []b;
}
int main()
{
int a[]={3,2,1,6,5,4};
for(int i=0;i<6;i++)
cout<<a[i]<<endl;
Mergesort(a,6);
for(int i=0;i<6;i++)
cout<<a[i]<<endl;
system("pause");
return 0;
}
擴展:自然和並排序
自然合併排序是合併排序算法的一種改進。
自然合併排序:對於初始給定的數組,通常存在多個長度大於1的已自然排好序的子數組段.例如,若數組a中元素爲{4,8,3,7,1,5,6,2},則自然排好序的子數組段有{4,8},{3,7},{1,5,6},{2}.用一次對數組a的線性掃描就足以找出所有這些排好序的子數組段.然後將相鄰的排好序的子數組段兩兩合併,構成更大的排好序的子數組段({3,4,7,8},{1,2,5,6}).繼續合併相鄰排好序的子數組段,直至整個數組已排好序。
程序實例:
#include<iostream>
using namespace std;
#include<vector>
static void merge(int array[], int p, int q, int r)
{
int i,k;
int begin1,end1,begin2,end2;
int* temp = new int [r-p+1]; //申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列
begin1= p; end1 = q; //設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置
begin2 = q+1; end2 = r;
k = 0;
//比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置
while((begin1 <= end1)&&( begin2 <= end2))
{
if(array[begin1]<array[begin2])
{
temp[k] = array[begin1]; begin1++;
}
else
{
temp[k] = array[begin2]; begin2++;
}
k++;
}
while(begin1<=end1) //若第一個序列有剩餘,直接拷貝出來粘到合併序列尾
{
temp[k++] = array[begin1++];
}
while(begin2<=end2) //若第二個序列有剩餘,直接拷貝出來粘到合併序列尾
{
temp[k++] = array[begin2++];
}
for (i = 0; i < (r - p +1); i++) //將排序好的序列拷貝回數組中
array[p+i] = temp[i];
delete[] (temp);
}
void Nature_Merge(int a[],int n)
{
vector<int> v;
v.push_back(0); //首作爲分割點
for(int i=0;i<n-1;i++) //中間的分割點
if(a[i]>a[i+1]) v.push_back(i);
v.push_back(n-1); //尾分割點
// for(int j=0;j<v.size();j++) //輸出測試分割點是否正確,可註釋掉
// cout<<v[j]<<endl;
// cout<<v.size()<<"ok"<<endl;
int s=1;
for(int group=v.size()-1;group!=1;group=(group%2==0?group/2:group/2+1))
{
int count=group/2; //合併次數 例如:5組合並需要兩次,4組合並兩次
//進行第一次合併
int p,q,r;
p=0;q=s;r=2*s;
if(r>v.size()-1) r=v.size()-1;
merge(a,v[p],v[q],v[r]);
//進行接下來的合併
for(int j=1;j<count;j++)
{
p=r;q=p+s;r=q+s;
if(r>v.size()-1) r=v.size()-1;
merge(a, v[p]+1, v[q],v[r]);
}
s+=s;
}
}
int main()
{
int a[]={4,3,2,1};
Nature_Merge(a,4);
for(int i=0;i<4;i++)
cout<<a[i]<<endl;
system("pause");
return 0;
}
總結:自然歸併排序思想簡單,但是實現的時候還有很多細節需要考慮,比如需要歸併次數是分組次數除以二取下限。還有就是r的控制,每次都要保證在v.size()-1範圍內不能超出。實現時候可能需要不斷測試,最終成功。
參考網站:http://blog.163.com/guchonglin-6/blog/static/5752753120099247170200/