//归并排序 复杂度O(n*logn)
int MergeSort(int a[], int n)
{
int *tmp = malloc(sizeof(int)*n);
if(tmp == NULL)
return 0;
mergesort(a,0,n-1,tmp);
free(tmp);
return 1;
}
//所谓的“归” 采用递归思想
void mergesort(int a[],int first, int last, int tmp[])
{
int mid = (first + last)/2;
if(first < last)
{
mergesort(a,first,mid,tmp); //排序左边
mergesort(a,mid+1,last,tmp); //排序右边
MergeTwoArray(a,first,mid,last,tmp); //将已经排序好的左右两边“并”起来
}
}
//所谓的“并”
void MergeTwoArray(int a[],int first, int mid, int last, int tmp[])
{
int i = first, j = mid + 1;
int k = 0;
while(i <= mid && j <= last)
{
if(a[i] <= a[j])
{
tmp[k++] = a[i++];
}
else
{
tmp[k++] = a[j++];
}
}
if(i<=mid)
{
while(i<=mid)
{
tmp[k++] = a[i++];
}
}
if(j<=last)
{
while(j<=last)
{
tmp[k++] = a[j++];
}
}
for(i=0;i<k;i++)
{
a[first+i]=tmp[i];
}
}
--------------------------------------------------------------- 8月28日看了MIT算法的归并排序 对“并”进行代码优化-------------------------------------------------------------
void MergeTwoArray(int a[],int first, int mid, int last, int tmp[]) { for(int k=first;k<=last;k++) { tmp[k] = a[k]; //copy } int i=first.j=mid+1; for(k=first;k<=last;k++) //merge { if(i>mid) a[k]=tmp[j++]; else if(j>last) a[k]=tmp[i++]; else if(tmp[i] < tmp[j]) a[k] = tmp[i++]; else a[k] = tmp[j++]; } }
优化归并排序:
一、考虑为何归并排序不适合特别小的数组?
因为申请额外空间,还会增加调用函数开销。而和插入排序比较而言,由于数组规模特别小,时间上并无太大差别。
因此改进方法就是进行切分。对于小于某个数组规模的数组进行插入排序。见代码
二、假设左右两边数组排序后已经是有序了,那就可以跳过合并的步骤,见代码
三、对于tmp辅助数组,可以不用来回拷贝,而是在这两个数组之间交替进行,只要保证最后拷贝回原来的数组即可。
void MergeTwoArray(int a[],int first, int mid, int last, int tmp[]) { for(int k=first;k<=last;k++) { tmp[k] = a[k]; //copy } int i=first.j=mid+1; for(k=first;k<=last;k++) { if(i>mid) a[k]=tmp[j++]; else if(j>last) a[k]=tmp[i++]; else if(tmp[i] < tmp[j]) a[k] = tmp[i++]; else a[k] = tmp[j++]; } }
#define CUTOFF 3 void merge_sort(int a[], int tmp[], int first, int last) { if(last <= first + CUTOFF - 1) { insert_sort(a,first,last); return; } int mid=first + (last-first)/2; merge_sort(a,tmp,first,mid); merge_sort(a,tmp,mid+1,last); if(a[mid] <= a[mid+1]) return; MergeTwoArray(a,tmp,first,mid,last); }
时间复杂度证明:
1.树状图法:
2.数学证明法:
3.归纳法
非递归的归并排序:
//非递归的归并排序
void MergeSort(int a[], int n)
{
int length = 1;
int *tmp = malloc(sizeof(int)*n);
if(tmp == NULL)
{
return 0;
}
while(length < N)
{
mergesort(A,tmp,N,length);
length *= 2;
mergesort(tmp,A,N,length);
length *= 2;
}
free(tmp);
return 1;
}
//所谓的并
void MergeTwoArray(int a[],int first, int mid, int last, int tmp[])
{
int i = first, j = mid + 1;
int k = 0;
while(i <= mid && j <= last)
{
if(a[i] <= a[j])
{
tmp[k++] = a[i++];
}
else
{
tmp[k++] = a[j++];
}
}
if(i<=mid)
{
while(i<=mid)
{
tmp[k++] = a[i++];
}
}
if(j<=last)
{
while(j<=last)
{
tmp[k++] = a[j++];
}
}
for(i=0;i<k;i++)
{
a[first+i]=tmp[i++];
}
}
//非递归的“归”
void mergesort(int a[],int tmp[],int N, int length)
{
for(int i=0;i <= N-2*length;i += 2*length)
{
MergeTwoArray(a,i,i+length,i+2*length-1,tmp);
}
if(i+length >= N)
{
for(int j=i;j<N;j++)
tmp[j] = a[j];
}
else
{
MergeTwoArray(a,i,i+length,i+2*length-1,tmp);
}
}
非递归精简版:
void merge_sort2(int a[], int N) { tmp = malloc(sizeof(int)*N); for(int sz=1;sz<N;sz*=2) { for(int lo=0;lo<N-sz;lo+=sz+sz) { MergeTwoArray(a,lo,lo+sz-1,min(lo+sz+sz-1,N-1),tmp); } } }