
- 一、归并排序的基本思想
- 二、递归实现归并
- 三、非递归实现归并
- 总结
一、归并排序的基本思想 1、归并排序算法是一类不同的排序方法,归并的含义是将两个或两个以上的有序数据序列合并成一个新的有序数据序列,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
2、基本思想是假设数组A有N个元素,数组A是N个有序的子序列组成,每个子序列的长度为1,两两重复合并,得到一个长度为N的有序数据序列为止。
3、合并算法的核心 *** 作就是将一维数组中前后相邻的两个两个有序序列合并成一个有序序列,合并算法也可以采用递归算法来实现。
归并排序的核心步骤: 二、递归实现归并
代码如下(示例):
void _MergeSort(int* a, int* tmp, int left, int right)//left为需要归并数组的左下标,tight同理
{
if (left >= right)//当满足条件时,表示只有一个元素,或者left已经在right的右边了,此时就不要再归并了
{
return;
}
int mid = left + (right - left) / 2;//每次我们都取中间下标,从而归并左区间和右区间,从而才能保证这次归并的左右区间是有序的
_MergeSort(a, tmp, left, mid);//归并左
_MergeSort(a, tmp, mid + 1, right);//归并右
//以下是正式归并的过程
int begin1 = left, end1 = mid;//begin1为这个区间的起点,end1为终点,being2和end2同理
int begin2 = mid + 1, end2 = right;
int index = left;//临时数组的下标,这里给第1个区间的下标,因为要保证拷贝数据时一一对应,否则将会出现数据覆盖的现象,归并将会失败
while (begin1 <= end1 && begin2 <= end2)//当一个区间没有数据了就结束
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
//上面的while循环,只有一个区间没有数据了,另一个还有,但我们不知道是哪一个,所以就对这两个区间都进行处理,以下的while循环只会进一个
while (begin1 <= end1)//区间1没结束,就将区间1剩下的数据放到临时数组
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)//区间2没结束,就将区间2剩下的数据放到临时数组
{
tmp[index++] = a[begin2++];
}
for (int i = left; i <= right; i++)//归并时,是从原数组左下标为left,右下标为right开始归并的
{ //存放数据时,在临时数组中,也是从左下标为left开始,直到右下标为right时结束
a[i] = tmp[i];//将临时数组的元素拷贝到原数组
}
}
void MergeSort(int* a, int n)//a为需要进行排序的数组的数组名,n为数组中元素的个数
{
int* tmp = (int*)malloc(sizeof(int)*n);//需要开辟新空间,用来临时存放归并好的数据。并在每次归并好后,拷会到原数组
_MergeSort(a, tmp, 0, n - 1);
}
三、非递归实现归并
非递归的实现方式,采用自底向上的思路。
对数组进行
1个一组进行拆分-排序合并;
2个一组进行拆分-排序合并;
4个一组进行拆分-排序合并;
8个一组的方式进行拆分…
代码如下(示例):
void MergeSortNonR(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
if (!tmp)
{
printf("malloc failn");
exit(-1);
}
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
//每次都分为左右区间 [i,i + gap - 1] 和 [i + gap,i + 2 * gap -1]
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
//控制边界
//end1、begin2、end2都可能越界 所以分三种情况 begin1不可能越界->i < n
//当end1越界,[begin2, end2]不存在
if (end1 >= n)//进行边界修正
{
end1 = n - 1;
}
//当[begin1, end1]存在 [begin2, end2]存在
if (begin2 >= n)
{
begin2 = n - 1;
end2 = n - 1;
}
//当end1和begin2没有越界,但end2越界
if (end2 >= n)
{
end2 = n - 1;
}
int index = i;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
}
for (int i = 0; i < n; i++)
{
a[i] = tmp[i];
}
gap *= 2;
}
}
//这种写法比上面的稍微好理解
void MergeSortNonR(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
if (!tmp)
{
printf("malloc failn");
exit(-1);
}
int gap = 1;
while (gap < n)
{
for (int i = 0; i < n; i += 2 * gap)
{
//每次都分为左右区间 [i,i + gap - 1] 和 [i + gap,i + 2 * gap -1]
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
//控制边界
//end1、begin2、end2都可能越界 所以分三种情况 begin1不可能越界->i < n
if (end1 >= n || begin2 >= n)
{
break; // end1越界 或者 begin2 越界都不需要归并
}
//当end1和begin2没有越界,但end2越界
if (end2 >= n)
{
end2 = n - 1;
}
int index = i;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
//每次归并小区间都拷贝回原数组
for (int j = i; j <= end2; j++)
{
a[j] = tmp[j];
}
}
gap *= 2;
}
}
总结
1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)