由于准备复习考研,趁闲暇时练练算法,偶然看到一篇归并排序的文章,提起了兴趣,于是捣腾一番,查阅了一些资料以及几位大神的博客,做以下记录。
参考资料:
白话经典算法系列之五 归并排序的实现
http://blog.csdn.net/morewindows/article/details/6678165
《大话数据结构》第9章 排序 9.8 归并排序(上)
http://www.cnblogs.com/cj723/archive/2011/04/25/2026751.html
《大话数据结构》第9章 排序 9.8 归并排序(下)
http://www.cnblogs.com/cj723/archive/2011/04/26/2026886.html
1、算法思想:
[数据结构]中要合并两个有序线性表时,每次取出头部较小的一个加入合并的表后面,再读其下一个,最后剩下的那个线性表中的所有元素全部加速合并表后面。
2、利用这种思想对数据排序
归:利用递归的思想,将一个数组、链表一直均等分割下去,直到每个组只剩下一个为止;
并:将之前分割后的数组两两之间合并,结果再两两合并....一直合并下去,直到只剩下一个数组。
3、源代码之递归实现
#include <stdio.h>
#include "list_sort.h"
//merge a segment of an array, minimum at the first
void merge1( int *arr, int first, int mid, int last, char *temp )
{
int i = first;
int j = mid + 1;
int k = 0;
while ( i <= mid && j <= last )
{
if ( arr[i] < arr[j])
temp[k++] = arr[i++];
else
temp[k++] = arr[j++];
}
while ( i <= mid )
temp[k++] = arr[i++];
while ( j <= last )
temp[k++] = arr[j++];
for ( k = first; k <= last; k++ )
arr[k] = temp[k - first];
}
//use temp to save memory
void mergeSort1( int *arr, int first, int last, char *temp )
{
if ( first < last )
{
int mid = (first + last) / 2;
mergeSort1 ( arr, first, mid, temp );
mergeSort1 ( arr, mid + 1, last, temp );
merge1 ( arr, first, mid, last, temp );
}
}
void merge_Array_Sort1(int *arr, int len)
{
char temp[len + 1];
mergeSort1(arr, 0, len - 1, temp);
}
4、递归实现的时间复杂度和空间复杂度分析
整个归并排序需要将数组拆分log2N次,即调用了log2N次递归函数。
每次递归都对N个数据进行了操作。所以,时间复杂度为:O(N*logN)
由于栈有log2N层,每一层总共要维护N个数据,所以空间复杂度也是:O(N*logN)
这样看来,其时间复杂度、空间复杂度并不高。
递归算法易于读懂、实现起来较为容易,但是其空间复杂度仍可进一步下降。所以我再用非递归的方法实现了归并排序。
5、非递归的归并排序
#include <stdio.h>
#include <stdlib.h>
#include "list_sort.h"
void merge2( int *arr1, int *arr2, int first, int mid, int last )
{
int i = first;
int j = mid + 1;
int k = first;
while ( i <= mid && j <= last )
{
if ( arr1[i] < arr1[j])
arr2[k++] = arr1[i++];
else
arr2[k++] = arr1[j++];
}
while ( i <= mid )
arr2[k++] = arr1[i++];
while ( j <= last )
arr2[k++] = arr1[j++];
}
//将arr1(长度为len),每段间隔为s,两两合并到arr2
void mergeSort2( int *arr1, int *arr2, int len, int s )
{
int i = 0;
int j = 0;
int first;
int mid;
int last;
//所有长度为s的分段两两合并
int times = len / (2 * s);
for ( i = 0; i < times; i++ )
{
first = 2 * i * s;
mid = first + s - 1;
last = first + 2 * s - 1;
merge2( arr1, arr2, first, mid, last);
}
//倒数第二段长度为s,最后一段长度大于0、小于s
int remain = len % (s * 2);
first = 2 * times * s;
if ( remain > s )
{
mid = first + s - 1;
last = len - 1;
merge2( arr1, arr2, first, mid, last );
}
else
for( j = first; j < len; j++ )
arr2[j] = arr1[j];
}
void merge_Array_Sort2( int *arr, int len )
{
int i;
int *temp = (int *)malloc(sizeof(int) * len);
int s = 1;
while (s < len)
{
mergeSort2 ( arr, temp, len, s );
s *= 2;
for (i = 0; i < len; i++)
{
arr[i] = temp[i];
}
}
}
易得其空间复杂度为N。