归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。归并排序是一种稳定的排序方法。
时间复杂度O(n log n)
空间复杂度T(n)
首先归并排序分为递归和非递归两种方式。
第一种是递归。
这种递归方式是将两个有序数组进行合并,那么时间复杂度就是O(N)了,我们还需要一个临时数组来存排序好的数组的值,最后将临时数组赋给原来的数组。
我们来看一下代码
void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex)
{
int midIndex;
if(startIndex < endIndex)
{
midIndex = startIndex + (endIndex-startIndex) / 2;//避免溢出int
MergeSort(sourceArr, tempArr, startIndex, midIndex);//递归排序左边序列
MergeSort(sourceArr, tempArr, midIndex+1, endIndex);//递归排序右边序列
Merge(sourceArr, tempArr, startIndex, midIndex, endIndex);//合并
}
}
int main()
{
int a[8] = {50, 10, 20, 30, 70, 40, 80, 60};
int i, b[8];
MergeSort(a, b, 0, 7);
for(i=0; i<8; i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
我们再来看一下合并函数:
void Merge(int sourceArr[],int tempArr[], int startIndex, int midIndex, int endIndex)
{
int i = startIndex, j=midIndex+1, k = startIndex;
while(i<=midIndex && j<=endIndex)
{
if(sourceArr[i] > sourceArr[j])
tempArr[k++] = sourceArr[j++];
else
tempArr[k++] = sourceArr[i++];
}
while(i <= midIndex)
tempArr[k++] = sourceArr[i++];
while(j <= endIndex)
tempArr[k++] = sourceArr[j++];
for(i=startIndex; i<=endIndex; i++)
sourceArr[i] = tempArr[i];
}
总代码如下:
#include <stdlib.h>
#include <stdio.h>
void Merge(int sourceArr[],int tempArr[], int startIndex, int midIndex, int endIndex)
{
int i = startIndex, j=midIndex+1, k = startIndex;
while(i<=midIndex && j<=endIndex)
{
if(sourceArr[i] > sourceArr[j])
tempArr[k++] = sourceArr[j++];
else
tempArr[k++] = sourceArr[i++];
}
while(i <= midIndex)
tempArr[k++] = sourceArr[i++];
while(j <= endIndex)
tempArr[k++] = sourceArr[j++];
for(i=startIndex; i<=endIndex; i++)
sourceArr[i] = tempArr[i];
}
void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex)
{
int midIndex;
if(startIndex < endIndex)
{
midIndex = startIndex + (endIndex-startIndex) / 2;//避免溢出int
MergeSort(sourceArr, tempArr, startIndex, midIndex);
MergeSort(sourceArr, tempArr, midIndex+1, endIndex);
Merge(sourceArr, tempArr, startIndex, midIndex, endIndex);
}
}
int main()
{
int a[9] = {50, 10, 20, 30, 70, 40, 80, 60,100};
int i, b[9];
MergeSort(a, b, 0, 8);
for(i=0; i<9; i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
第二种是非递归算法
我们来看一下步骤
因为非递归是不断调用栈的,所以不想递归那样占用内存很大。
首先是排序函数
void Merge_sort(int a[],int N)
{
int length=1;
int *temp;
temp=(int*)malloc(N*sizeof(int));
if(temp!=NULL)
{
while(length<N)
{
Merge_pass(a,temp,N,length);//先从最小的序列开始
length*=2;//每次加倍
}
free(temp);//最后释放,不占用空间
}
else
printf("空间不足");
}
然后是内部函数
void Merge_pass(int a[],int temp[],int N,int length)
{
int i;
for(i=0;i<=N-2*length;i+=2*length)//每次加倍,因为每次都是偶数序列,最后可能保留一个序列。
{
Merge(a,temp,i,i+length,i+2*length-1);//合并有序序列
}
if(i+length<N)//是偶数序列
{
Merge(a,temp,i,i+length,N-1);
}
}
关于合并的函数
void Merge(int a[],int temp[],int L,int R,int RightEnd)
{
/* 将有序的A[L]~A[R-1]和A[R]~A[RightEnd]归并成一个有序序列 */
int Leftend,t;
int i;
t=L;
Leftend=R-1;//左边最后一个元素位置
while(L<=Leftend&&R<=RightEnd)
{
if(a[L]>a[R])
temp[t++]=a[R++];
else
temp[t++]=a[L++];
}
while(L<=Leftend)temp[t++]=a[L++];
while(R<=RightEnd)temp[t++]=a[R++];
for(i=RightEnd;i>=0;i--)
a[i]=temp[i];
}
总代码如下
#include <stdlib.h>
#include <stdio.h>
void Merge_sort(int a[],int N);
void Merge_pass(int a[],int temp[],int N,int length);
void Merge(int a[],int temp[],int L,int R,int RightEnd);
int main()
{
int a[9]={1,3,4,2,5,6,7,11,8};
Merge_sort(a,9);
for(int i=0;i<9;i++)
printf("%d",a[i]);
return 0;
}
void Merge_sort(int a[],int N)
{
int length=1;
int *temp;
temp=(int*)malloc(N*sizeof(int));
if(temp!=NULL)
{
while(length<N)
{
Merge_pass(a,temp,N,length);
length*=2;
}
free(temp);
}
else
printf("空间不足");
}
void Merge_pass(int a[],int temp[],int N,int length)
{
int i;
for(i=0;i<=N-2*length;i+=2*length)
{
Merge(a,temp,i,i+length,i+2*length-1);
}
if(i+length<N)
{
Merge(a,temp,i,i+length,N-1);
}
}
void Merge(int a[],int temp[],int L,int R,int RightEnd)
{
int Leftend,t;
int i;
t=L;
Leftend=R-1;//左边最后一个元素位置
while(L<=Leftend&&R<=RightEnd)
{
if(a[L]>a[R])
temp[t++]=a[R++];
else
temp[t++]=a[L++];
}
while(L<=Leftend)temp[t++]=a[L++];
while(R<=RightEnd)temp[t++]=a[R++];
for(i=RightEnd;i>=0;i--)
a[i]=temp[i];
}