图解排序算法(四)之归并排序

定义


归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。  将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。


基本思想


归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。


分而治之

数据结构(七)排序---归并排序_递归


实现


可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n。


递归实现


(1)“分解”——将序列每次折半划分。  (2)“合并”——将划分后的序列段两两合并后排序。


#define MAXSIZE 10  void Merging(int L1[], int L1_size, int L2[], int L2_size) {     int temp[MAXSIZE], i, j, k;     i = j = k = 0;     //按照大小放入temp数组     while (i < L1_size && j < L2_size)     {         if (L1[i] >= L2[j])             temp[k++] = L2[j++];         else             temp[k++] = L1[i++];     }     //对未处理完的数据全部放入temp数组     for (; i < L1_size; i++)         temp[k++] = L1[i];     for (; j < L2_size; j++)         temp[k++] = L2[j];     //将局部变量数据存放入L1中     for (i = 0; i < (L1_size + L2_size); i++)         L1[i] = temp[i]; }  void MergeSort(int k[], int n) {     if (n>1)     {         int *list1 = k;         int list1_size = n / 2;         int *list2 = k + n / 2;         int list2_size = n - list1_size;          MergeSort(list1, list1_size);         MergeSort(list2, list2_size);         Merging(list1, list1_size, list2, list2_size);     } }  int main() {     int i;     int a[10] = { 5, 2, 6, 0, 3, 9, 1, 7, 4, 8 };     MergeSort(a, 10);      for (i = 0; i < 10; i++)         printf("%d ", a[i]);      system("pause");     return 0; }


非递归实现

数据结构(七)排序---归并排序_归并排序_02


void MergeSort2(int k[], int n) {     int left_min, left_max, right_min, right_max;     int *temp = (int*)malloc(sizeof(int)*n);     int next;     int step = 1;     //步长从1到n/2     for (; step < n;step*=2)     {         for (left_min = 0; left_min < n-step;left_min=right_max)         {             right_min = left_max = left_min + step;             right_max = right_min + step;             if (right_max > n)                 right_max = n;             next = 0;             while (left_min<left_max&&right_min<right_max)             {                 if (k[left_min] < k[right_min])                     temp[next++] = k[left_min++];                 else                     temp[next++] = k[right_min++];             }             for (; left_min < left_max; )                 k[--right_min] = k[--left_max];             for (; next > 0;)                 k[--right_min] = temp[--next];         }     } }


性能分析


归并排序是稳定排序,它也是一种十分高效的排序,能利用完全二叉树特性的排序一般性能都不会太差。
从上文的图中可看出,每次合并操作的平均时间复杂度为O(n),而完全二叉树的深度为|log2n|。总的平均时间复杂度为O(nlogn)。
而且,归并排序的最好,最坏,平均时间复杂度均为O(nlogn)。