归并排序

  #返回上一级

@Author: 张海拔

@Update: 2014-01-14

@Link: http://www.cnblogs.com/zhanghaiba/p/3518324.html

 

 1 /*

 2  *Author: ZhangHaiba

 3  *Date: 2014-1-13

 4  *FIle: merge_sort.c

 5  *

 6  *merge sort demo

 7  */

 8 

 9 #include <stdio.h>

10 #define N 512

11 #define INF 0x7fffffff //sentinel

12 

13 void merge_sort(int *, int, int);

14 void set_array(int *, int);

15 void show_array(int *, int);

16 

17 int array[N];

18 int left_tmp[N/2 + 10]; //save INF to a[array_len] as sentinel

19 int right_tmp[N/2 + 10];

20 

21 int main(void)

22 {

23     int n;

24 

25     scanf("%d", &n);

26     set_array(array, n);

27     merge_sort(array, 0, n-1);

28     show_array(array, n);

29     return 0;

30 }

31 

32 

33 void merge_sort(int *a, int l, int r)

34 {

35     if (l >= r) return;

36     else {

37         int m = l + (r-l)/2; //partition

38         merge_sort(a, l, m);

39         merge_sort(a, m+1, r);

40         int left_len = m-l+1, right_len = r-m, i, j = l;

41         for (i = 0; i < left_len; ++i, ++j)

42             left_tmp[i] = a[j];

43         left_tmp[i] = INF;

44         for (i = 0; i < right_len; ++i, ++j)

45             right_tmp[i] = a[j];

46         right_tmp[i] = INF;

47         for (i = j = 0; l <= r; ++l) //merge order list    

48             a[l] = left_tmp[i] <= right_tmp[j] ? left_tmp[i++] : right_tmp[j++];

49     }

50 }

51 

52 void set_array(int *a, int n)

53 {

54     int i;

55 

56     for (i = 0; i < n; ++i)

57         scanf("%d", a+i);

58 }

59 

60 

61 void show_array(int *a, int n)

62 {

63     int i;

64 

65     for (i = 0; i < n; ++i)

66         printf(i == n-1 ? "%d\n" : "%d ", a[i]);

67 }

 

归并排序,是分治法的典范。

如果你理解了二叉树的前序遍历和后序遍历,就不难写出归并排序。

归并和快排一样,有一个划分过程。不过这里的归并完全是二分的。划分在前序遍历过程中(边划分边处理子数组),而归并体现在后序遍历过程中。

归并过程:首先复制左右子数组分别放在left_tmp和right_tmp数组中,然后通过有序表的合并算法放回根数组[l, r]中。

显然归并过程是从叶子数组开始的(回溯开始于前进的结束),而叶子数组只有一个元素,必定是有序的。所以说归并的过程核心是有序表的合并算法。

有序表的合并算法其时间复杂度显然是O(n),从整个二分归并树来看,横向看子数组的连起来的长度肯定是n,纵向看树高为lgn。

所以算法的时间复杂度相当稳定,就是O(n*lgn)。

 

上述实现中,注意:

(1)引入INF存入临时数组有效数据的下一个位置作为哨兵,这样方便简单实现有序表合并算法,因此临时数组长度应该略大于N/2,防止极端情况数组越界。

(2)归并排序一大特征就是:该算法是稳定的。所以在有序表合并时,注意要用"<="而非"<",这样才能确保相同关键字排序后相对前后位置不会改变。

 

  #返回上一级

你可能感兴趣的:(归并排序)