归并排序,不单单是归并排序,应该更重要的是一种分治的思想。把问题大而化小,再将小问题的结果汇总,得到大问题的解。
递归的写法更能体现归并的分治思想,但是递归调用,当层次非常多的时候,会影响性能,现在考虑如何把归并的递归方式变成非递归方式。
归并的完整描述是:
假设初始序列含有n个记录,则可看成是n个有序的子序列,每个子序列的长度为1, 然后两两归并,得到[n/2]个升序为2(或1,最后一个可能为1)的子序列,再将此序列两两归并,得到 [n/4]个长度为4(或者<4)的子序列, .....直到整个序列有序为止,此为2-路归并排序。
按照此方法,写非递归方法时,就不难了。即我们按照子序列的步长,从1 到 n, 为n时,序列已有序,进行两两归并即可。
如:目录待排序的序列为: (2) (27) (454) (23) (76) (13) (30)
1趟子序列长序为1 ---------- ---------------- --------------- ----
第一趟两两归并,结果为: (2 27) (23 454) (13 76) (30)
2趟子序列长序为2 ------------------------------- --------------------
第二趟两两归并,结果为:(2 23 27 454) (13 30 76)
3趟子序列长度为4 ---------------------------------------------------
第三趟两两归并,结果为 :(2 13 23 27 30 76 454)
简单的参考代码如下:
- /**
- * 非递归的归并排序
- */
- #include <stdio.h>
- //sr[i,m] 和 sr[m+1, n]都有序,现在把它们合并成整体有序存放到tr[i, n]中
- void merge(int sr[], int tr[], int i, int m, int n)
- {
- int j, k, t_pos;
- for (j=m+1, k=i; i<=m&&j<=n; k++){
- if (sr[i]<=sr[j]){
- tr[k] = sr[i];
- i++;
- } else {
- tr[k] = sr[j];
- j++;
- }
- }
- // copy the rest elements
- if (i<=m){
- // tr[k..n] = sr[i..m]
- for (; i<=m; i++, k++)
- tr[k] = sr[i];
- } else if (j<=n){
- // tr[k..n] = sr[j..n]
- for (; j<=n; j++, k++)
- tr[k] = sr[j];
- }
- }
- //将长度为s的子序列进行两两合并
- void merge_len(int src[], int dst[], int s, int len)
- {
- int i = 0;
- while (i <= len-2*s){ //保证剩余的长度>=2s ; len-i >= 2s ==> i<=len-2s
- merge(src, dst, i, i+s-1, i+2*s-1);
- i = i+2*s;
- }
- if (len-i>s){//剩余两段有序部分
- merge(src, dst, i, i+s-1, len-1);
- } else { //只剩余一段,直接复制到目标数组中
- for (; i<len; i++)
- dst[i] = src[i];
- }
- }
- #define N 100
- void merge_sort(int arr[], int len)
- {
- int s = 1, i;
- int tmp[N] = {0};
- while (s<len){
- merge_len(arr, tmp, s, len);
- s += s;//步长增大
- merge_len(tmp, arr, s, len);
- s += s;
- }
- }
- int main()
- {
- int arr[N] = {0};
- int n, i;
- printf("Please input arr size: ");
- scanf("%d", &n);
- printf("Please input arr elements: ");
- for (i=0; i<n; i++)
- scanf("%d", &arr[i]);
- merge_sort(arr, n);
- printf("Sort results: \n");
- for (i=0; i<n; i++)
- printf("%d\t", arr[i]);
- printf("\n");
- return 0;
- }