9.4 归并排序(排序(上))
归并排序(稳定)
文章目录
- 归并排序(稳定)
- 9.1 基本概念
- 9.2 实现
- 9.2.1 递归算法
- 9.2.2 非递归算法
- 9.3 内容补充
9.1 基本概念
核心:有序子列的归并
具体步骤:
- 从左至右依次遍历两个数组
- 比较每次两数组元素大小,并将较小的元素存入新数组中
- 不断操作至遍历结束
伪代码描述:
时间复杂度:O( N )
/*
**作用:归并排序
**注意:L = 左边起始位置,R = 右边起始位置,right_end = 右边重点位置
*/
void merge(element_type A[], element_type tempA[], int L, int R, int right_end)
{left_end = R - 1; //左边终点位置。假设两子列紧挨tmp = L; //过渡数组下标的初始位置element_num = right_end - L + 1;//任一子列未复制完就继续循环while (L <= left_end && R <= right_end){if (A[L] <= A[R])tempA[tmp++] = A[L++];elsetempA[tmp++] = A[R++];}while (L <= left_end)tempA[tmp++] = A[L++]; //复制左子列剩余元素while (R <= right_end)tempA[tmp++] = A[R++]; //复制有子列剩余元素//将过渡数组赋值会Afor (; element_num > 0; element_num--)A[--element_num] = tempA[--element_num]
}
9.2 实现
9.2.1 递归算法
分而治之:不断递归调用,将原数组不断分成等长的两个子列,再对两个子列进行归并排序
代码实现:
时间复杂度:O( NlogN )
/*
**作用:将有序的A[L]~A[R-1]和A[R]~A[right_end]归并成一个有序数列
**注意:L = 左边起始位置,R = 右边起始位置, right_end = 右边终点位置
*/
void merge(element_type A[], element_type tmpA[], int L, int R, int right_end)
{int temp, left_end, element_num;temp = L; //过渡数组其实位置left_end = R - 1; //左子列终点下标element_num = right_end - L + 1;while (L <= left_end && R <= right_end){if (A[L] <= A[R])tmpA[temp++] = A[L++]; //将左边元素复制到tmpAelsetmpA[temp++] = A[R++]; //将右边元素复制到tmpA}while (L <= left_end)tmpA[temp++] = A[L++]; //复制剩下的左子列元素while (R <= right_end)tmpA[temp++] = A[R++]; //复制剩下的右子列元素for (int i = 0; i < element_num; i++, right_end--)A[right_end] = tmpA[right_end];
}/*
**作用:归并排序的递归算法
**注意:在左右两边分别递归时,传入的center值不能相同,否则会导致元素重复
*/
void MSort(element_type A[], element_type tmpA[], int L, int right_end)
{int center;if (L < right_end){center = (L + right_end) / 2;MSort(A, tmpA, L, center); //递归解决左边MSort(A, tmpA, center+1, right_end); //递归解决右边merge(A, tmpA, L, center, right_end); //合并最后两段子序列}
}/*
**作用:归并排序
*/
void merge_sort(element_type A[], int N)
{element_type *tmpA;tmpA = (element_type*)malloc(N * sizeof(element_type));if (tmpA != NULL){MSort(A, tmpA, 0, N-1);free(tmpA); //排序结束后释放过渡数组空间}else printf("空间不足");
}
9.2.2 非递归算法
每次分别对两个子序列进行归并,不断循环logN次
代码实现:
时间复杂度:O( NlogN )
/*
**作用:将有序的A[L]~A[R-1]和A[R]~A[right_end]归并成一个有序数列
**注意:1. L = 左边起始位置,R = 右边起始位置, right_end = 右边终点** 位置
** 2. 为了减少数组间互相复制的次数,此merge函数不将过渡数组的值复制** 给A[]
*/
void merge(element_type A[], element_type tmpA[], int L, int R, int right_end)
{int temp, left_end, element_num;temp = L; //过渡数组其实位置left_end = R - 1; //左子列终点下标element_num = right_end - L + 1;while (L <= left_end && R <= right_end){if (A[L] <= A[R])tmpA[temp++] = A[L++]; //将左边元素复制到tmpAelsetmpA[temp++] = A[R++]; //将右边元素复制到tmpA}while (L <= left_end)tmpA[temp++] = A[L++]; //复制剩下的左子列元素while (R <= right_end)tmpA[temp++] = A[R++]; //复制剩下的右子列元素
}/*
**作用:归并排序的循环算法,两两归并相邻有序子列
**注意:1. length = 当前有序子列的长度
*/
void merge_pass(element_type A[], element_type tmpA[], int N, int length)
{int i;//注意:为了防止访问数组越界,i < N - 2*lengthfor (i = 0; i < N - 2 * lenght; i += length)//注意right_end,需要减去1merge(A, tmpA, i, i + length, i + length * 2 - 1);if (i + length < N) //剩余两个子列merge(A, tmpA, i,i + length, N - 1); //归并最后两个子列else //仅剩一个子列for (; i < N; i++)tmpA[i] = A[i];
}/*
**作用:归并排序
*/
void merge_sort(element_type A[], int N)
{int length;element_type *tmpA;length = 1; //子序列初始长度为1tmpA = (element_type*)malloc(N * sizeof(element_type));if (tmpA != NULL){while (length < N){merge_pass(A, tmpA, N, length);length *= 2;merge_pass(tmpA, A, N, length);length *= 2;}free(tmpA);}else printf("空间不足");
}
9.3 内容补充
1. CSDN:一文读懂『归并排序』算法(Merge Sort)(含算法模板)
2. 博客园:归并排序详解及应用