归并排序是递归的一个典型应用,是用空间换时间的典型例子(需要一个临时数组存放以排序的序列)。在各个排序算法中其比较的次数是最少的,在最坏的情况下的运行时间是O(NlogN)。
归并排序是一个递归-合并的过程,基本操作是将两个已经排好序的表扫描一遍依次有序的存放到第三个表中。在递归的过程中,每次递归都是一次划分,将一个序列从中划分成两个子序列(2路归并),直到子序列中元素数为1,然后开始两两合并成有序的序列。基于这个想法引申到多路归并。
归并排序的过程:
初始序列:
申请临时数组:
开始递归:
递归ing
合并:
写回原数组:
合并-写回(两步)
合并
省略一步写回->合并两个子序列
合并:
对右边的序列执行上边的递归步骤...
最终写回原序列,完成排序:
显然归并使用的是分治的策略,从上边排序的过程可以看出来二路归并类似于一个后序遍历的过程,这样的话用树形表示会更直接一些:
递归过程:
开始合并:
右子树到了叶节点:
合并:
之后一直重复这个过程,知道回到根节点上。由此可以看出他的最坏时间复杂度是O(NlogN)。
源码:
void merge(int arr[], int tmp_arr[], int left_pos, int right_pos, int right_end) { int i , left_end, num_count, tmp_pos; left_end = right_pos - 1; num_count = right_end - left_pos + 1; tmp_pos = left_pos; /*将两个要合并的序列,较小者先放入临时数组中*/ while(left_pos <= left_end && right_pos <= right_end) { if(arr[left_pos] <= arr[right_pos]) { tmp_arr[tmp_pos++] = arr[left_pos++]; } else { tmp_arr[tmp_pos++] = arr[right_pos++]; } } while(left_pos <= left_end) { tmp_arr[tmp_pos++] = arr[left_pos++]; } while(right_pos <= right_end) { tmp_arr[tmp_pos++] = arr[right_pos++]; } /*把临时数组中的数据copy回原数组*/ for(i = 0; i < num_count; i++, right_end--) { arr[right_end] = tmp_arr[right_end]; } }
void msort(int arr[], int tmp_arr[], int left, int right) { int center; if(left < right) { center = (left + right) / 2; msort(arr, tmp_arr, left, center); msort(arr, tmp_arr, center + 1, right); merge(arr, tmp_arr, left, center + 1, right); } }
/*驱动函数*/ void merge_sort(int arr[], int count) { int *tmp_arr; tmp_arr = (int *)malloc(count * sizeof(int)); if(tmp_arr != NULL) { msort(arr, tmp_arr, 0, count-1); free(tmp_arr); } else { printf("error:alloc tempt array failed\n"); return; } }
将前几次排序使用的随机数文件拿过来测试了一下
10w数字时候排序的时间:
90w数字时候排序的时间:
时间: 2024-11-05 04:48:54