前面看完了插入排序和插入排序后面习题里面的选择排序,最近又把归并排序给看了。
插入排序的最坏情况为n平方,当然选择排序的恒为n平法,而归并排序则是一个nlogn的时间复杂度。当然这指的是在n无限大的情况下才可以成立,对于元素个数很小的,插入排序反而会快一些。
归并排序的主要思想就是利用了分治法把大的问题划分成相同类型,比较小规模的子问题来解决。
下面贴今天敲得代码:(在对排序好的数组进行Merge的时候,采用了两种方法,区别在于是否采用了监视哨)
//MergeSort
#include <iostream>
using namespace std;
const int MAX = 0x3f3f3f3f;
void Merge(int a[],int p,int q,int r)//带有监视哨的写法
{
int Lsize = q - p +1;
int Rsize = r - q;
int *L = new int[Lsize+1];
int *R = new int[Rsize+1];
int i = 0,j = 0;
for(i = 0;i<Lsize;i++)
L[i] = a[p+i];
for(j = 0;j<Rsize;j++)
R[j] = a[q+1+j];
L[Lsize] = MAX;
R[Rsize] = MAX;
int Length = Lsize + Rsize ;
for(i=0,j=0;i+j<Length;)
{
if(L[i]<=R[j])
a[i+j+p] = L[i++];
else
a[i+j+p] = R[j++];//这地方容易出错
}
delete []R;
delete []L;
}
void Merge2(int a[],int p,int q,int r)//不带监视哨的写法 ,主体思想都是一样的
{
int Lsize = q - p +1;
int Rsize = r - q;
int *L = new int[Lsize];
int *R = new int[Rsize];
int i = 0,j = 0;
for(i = 0;i<Lsize;i++)
L[i] = a[p+i];
for(j = 0;j<Rsize;j++)
R[j] = a[q+1+j];
int Length = Lsize + Rsize ;
for(i=0,j=0;i<Lsize&&j<Rsize;)
{
if(L[i]<=R[j])
a[i+j+p] = L[i++];
else
a[i+j+p] = R[j++];//这地方容易出错
}
while(i==Lsize&&j<Rsize)//左部完成
a[i+j+p] = R[j++];
while(j==Rsize&&i<Lsize)
a[i+j+p] = L[i++];
delete []R;
delete []L;
}
void MergeSort(int a[],int p,int q)//p,q分别代表需用排序的起始与终结位置
{
if(p<q)
{
int middle = (p+q)/2;
MergeSort(a,p,middle);
MergeSort(a,middle+1,q);
Merge(a,p,middle,q);
//Merge2(a,p,middle,q);
}
}
int main()
{
int a[] = {1,2,3,123,12312,-123};
int length = sizeof(a)/sizeof(int);
MergeSort(a,0,length-1);
for(int i = 0;i<length;i++)
cout<<a[i]<<" ";
return 0;
}
在监视哨的使用中使用了MAX,来表示无穷大的情况。
查阅了相关的博客,如果我们已经知道了我们需要的数的范围,那么这个max的取值就很简单了。
对于一般不知道的情况,大部分采用的是0x7fffffff,这个值在直接比较时不会有任何问题,但是如果参与运算之后再去比较就会有溢出问题。
所以另一种是采用0x3f3f3f3f。这样表示的数在加完之后不会溢出。。
参见博客:
http://blog.chinaunix.net/uid-24701781-id-3418699.html
当然这个归并排序我已经看了好几遍,自己动手写的时候还是会出现错误。。。