排序之归并排序
在准备考研期间看数据结构课本时,又重温了一下各种排序算法,觉得很有收获。虽然有很多关于排序算法的很好的博客文章了,但自己也想再来blabla,加深一下记忆,废话少说,每天整理一点,没有顺序,一个一个的来。
关于归并排序,有必要先来说一下归并排序的思想:归并排序使用了分治策略,将一个大序列的排列问题分解为对两个或多个子序列的排列问题,然后再对子序列递归使用相同的方式进行排序;在子序列排好序后,将结果合并起来即可。
分治策略的关键是分解与合并,归并排序的分解部分很简单,直接将序列从中间分解开,但归并的时候需要小心!将两个排好序的子序列按次序进行合适的穿插,从而获得原序列的一个排序。
这里先给出将两个有序序列合并成一个有序序列的实现方法,多啰嗦几句:合并时我们保持两个指针,一个指向A序列里的下一个欲待归并的元素,一个指向B序列里的下一个欲待归并的元素,每步我们都对这两个指针指向的元素进行比较,小的元素归并到C序列,并且指向较小的元素的指针往后推进一个元素,另一个指针则保持不动。这样,不断循环,直到一个子序列为空为止,此时,另一个子序列剩下的元素直接复制到C序列末尾即可。此时C序列即为合并后的序列。实现代码如下:
voidMergeArray(int *a,int first, int mid, int last, int *temp)
{
inti=first,m=mid;
intj=mid+1,n=last;
intk=0;
while(i<=m&& j<=n)
{
if(a[i]<=a[j])
temp[k++]= a[i++];
else
temp[k++]= a[j++];
}
while(i<=m)
temp[k++]= a[i++];
while(j<=n)
temp[k++]= a[j++];
//代码到这里即实现了将两个有序子序列合并为一个有序序列,并保存在temp数组中,下面的几句代码是为了实现整个归并排序的合并部分的,因为需要和并多次,而不仅仅是一次!下面再详细说明
//把所有的按顺序的数字全部组合到数组a,a数组用来做保存用
for(i=0;i<k; i++)//注意这里是i<k,因为k是从零开始的,不要想错!
{
a[first+i]= temp[i];//注意i与first的区别
}
//该段代码保证了将每次合并后的数字按顺序保存到数组a中,直到所有的数字均有序为止,注意一定是a[first+i]!first+i保证了数组中数字的正确位置,如果不加first,在合并时会出错!
}
下面具体分析一下分解与合并的过程:
因为分治策略中使用了递归的思想,而这也正是分治策略的可行之处。因为通常我们如果对问题只进行一次分解,对于问题的求解并没有太大的帮助,因而需要使用递归把问题分解到足够小。本问题中我们就对子序列使用了递归调用的方式,先给出代码再来分析:
voidSort_Merge(int *a,int first, int last, int *temp)
{
if(first<last)
{
intmid = (first + last)/2;
Sort_Merge(a,first,mid,temp);
Sort_Merge(a,mid+1,last,temp);
MergeArray(a,first,mid,last,temp);
}
}
对于递归调用,我们需要给出递归出口,而在这里,因为我们需要合并的是有序的序列,因而,首先我们要把数列分解成有序序列。显然,当每个序列只有一个数字时肯定满足该序列是有序的,这也就是我们的递归出口,代码表示即为if(first<lasr)。当分解完成后(此时每个序列均为一个数字),对于符合递归出口的也就是不能再继续进行递归调用的序列进行合并,并不断向上推进,直到所有的数字均有序为止。举例示意一下:
现有如下需要排序的序列:
:
递归调用过程:
初始: (a,0,4,temp)
0<4 mid=2: (a,0,2,temp) (a,3,4,temp)
0<2 mid=1: (a,0,1,temp) (a,2,2,temp) 3<4 mid=3:(a,3,3,temp) (a,4,4,temp)
0<1 mid=0: (a,0,0,temp) (a,1,1,temp)
上述过程即表示了将序列通过递归的方式分解为不同的序列,每个序列开始只有一个数字,然后再进行逆向合并即可。(同一层上的序列进行合并,再向上推进,直到所有的数字通过合并变为有序序列)
下面给出完整的测试代码和运行截图(以后的就不再写了)
代码:
#include"iostream"
using namespace std;
//把两个按顺序的数字组合拼到一起
void MergeArray(int *a,int first, int mid, int last, int *temp)
{
int i=first,m=mid;
int j=mid+1,n=last;
int k=0;
while(i<=m &&j<=n)
{
if(a[i]<=a[j])
temp[k++] =a[i++];
else
temp[k++] =a[j++];
}
while(i<=m)
temp[k++] = a[i++];
while(j<=n)
temp[k++] = a[j++];
//把所有的按顺序的数字全部组合到数组a,a数组用来做保存用
for(i=0; i<k; i++)//注意这里是i<k,因为k是从零开始的,不要想错!
{
a[first+i] =temp[i];//注意i与first的区别
}
}
//分治与递归的使用
void Sort_Merge(int *a,int first, int last, int *temp)
{
if(first<last)
{
int mid = (first +last)/2;
Sort_Merge(a,first,mid,temp);
Sort_Merge(a,mid+1,last,temp);
MergeArray(a,first,mid,last,temp);
}
}
//main
int main()
{
int n;
cout<<"Please inputthe size of the array:";
cin>>n;
cout<<"Pleaseinput "<<n<<" numbers:"<<endl;
int *pia = new int[n];
for(int i=0; i<n; i++)
{
cin>>*pia;
pia++;
}
pia-=n;
int *put = new int[n];
Sort_Merge(pia,0,n-1,put);
cout<<"The rightorder of the array:"<<endl;
for(int j=0;j<n;j++)
{
cout<<*pia<<"";
pia++;
}
cout<<endl;
//delete [] pia;
return 0;
}
运行结果: