归并排序算法学习

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

时间复杂度为O(nlog2n),这是算法中最坏和平均的时间性能最好的一种,空间复杂度为O(n).
缺点就是比较占用内存,但却是一种效率高且稳定的算法(速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对

有序的数列)。
所谓归并排序是指两个或者两个以上有序的数列,合并成一个仍然有序的数列,这种排序经常用于多个有序的数据文件归并成一个有序的数

据文件。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和

右区间用一次归并操作合并成有序的区间[s,t]。

作了解:
归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11,;
逆序数为14;

言归正传,首先考虑下如何将将二个有序数列合并(先用二路归并实现)。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

我用java实现合并,然后顺便使用set去除相同元素,注意点:初始化易错,int a[] = new int[10]; Set<Integer> s = new HashSet<Integer>();其他的看代码注意;

    public static void main(String[] args) {
        int a[] = { 1, 3, 4, 5, 7 };
        int b[] = { 2, 3, 6, 8, 9 };
        int c[] = new int[10];// 初始化空间大小很重要
        int i, j, k;
        i = j = k = 0;
        int n = a.length;
        int m = b.length;
        while (i < n && j < m) {
            if (a[i] < b[j]) {
                c[k++] = a[i++];
            } else {
                c[k++] = b[j++];
            }
        }
        while (i < n) {
            c[k++] = a[i++];
        }
        while (j < m) {
            c[k++] = b[j++];
        }
        // Set是一个接口,不能直接实例化,HashSet实现了Set接口,所以可以new HashSet()
        Set<Integer> s = new HashSet<Integer>();
        for (int aa = 0; aa < c.length; aa++) {
            s.add(c[aa]);
            System.out.print(c[aa] + ",");
        }
        System.out.println();
        int aa = 0;// 用while循环实现上面的方法
        while (aa < c.length) {
            s.add(c[aa++]);

        }
        Iterator<Integer> it = s.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }

c语言实现:

void mArray(int a[],int n,int b[],int m,int c[]){

  int i,j,k;

  i=j=k=0;

  while(i<n && j<m){

    if(a[i] < b[j])

      c[k++]=a[i++];

    else

      c[k++]=b[j++];

  }

  while(i<n){

    c[k++]=a[i++];

  }

  while(j<m){

    c[k++]=b[j++];

  }

}

以下转自MoreWindows文章。

可以看出合并有序数列的效率是比较高的,可以达到O(n)。

解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?

可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递的分解数列,再合数列就完成了归并排序。

  1. //将有二个有序数列a[first...mid]和a[mid...last]合并。  自己调试有错误,下面是自己找的作法
  2. void mergearray(int a[], int first, int mid, int last, int temp[])  
  3. {  
  4.     int i = first, j = mid+1 ;  
  5.     int m = mid,   n = last;  
  6.     int k = 0;  
  7.       
  8.     while (i <= m && j <= n)  
  9.     {  
  10.         if (a[i] <= a[j])  
  11.             temp[k++] = a[i++];  
  12.         else  
  13.             temp[k++] = a[j++];  
  14.     }  
  15.       
  16.     while (i < m)  
  17.         temp[k++] = a[i++];  
  18.       
  19.     while (j < n)  
  20.         temp[k++] = a[j++];  
  21.       
  22.     for (i = 0; i < k; i++)  
  23.         a[first + i] = temp[i];  
  24. }  
  25. void mergesort(int a[], int first, int last, int temp[])  
  26. {  
  27.     if (first < last)  
  28.     {  
  29.         int mid = (first + last) / 2;  
  30.         mergesort(a, first, mid, temp);    //左边有序  
  31.         mergesort(a, mid + 1, last, temp); //右边有序  
  32.         mergearray(a, first, mid, last, temp); //再将二个有序数列合并  
  33.     }  
  34. }  
  35.   
  36. bool MergeSort(int a[], int n)  
  37. {  
  38.     int *p = new int[n];  
  39.     if (p == NULL)  
  40.         return false;  
  41.     mergesort(a, 0, n - 1, p);  
  42.     delete[] p;  
  43.     return true;  
  44. }  

 

归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)。因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(N*logN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)也是效率比较高的。

 

在本人电脑上对冒泡排序,直接插入排序,归并排序及直接使用系统的qsort()进行比较(均在Release版本下)

对20000个随机数据进行测试:

对50000个随机数据进行测试:

再对200000个随机数据进行测试:

 

注:有的书上是在mergearray()合并有序数列时分配临时数组,但是过多的new操作会非常费时。因此作了下小小的变化。只在MergeSort()中new一个临时数组。后面的操作都共用这一个临时数组。

最后附上自己的java实现归并的代码:

    public static void main(String[] args) {
        // int a[] = { 6, 4, 7, 5, 8 };
        int a[] = { 25, 10, 7, 19, 3, 48, 12, 17, 56, 30, 21 };
        int p[] = new int[a.length + 1];
        doMerge(a, 0, a.length, p);
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + "--");
        }
    }

    public static void doMerge(int[] a, int frist, int last, int[] p) {
        if (frist + 1 < last) {
            int mid = (frist + last) / 2;
            doMerge(a, frist, mid, p);
            doMerge(a, mid, last, p);
            mergeArray(a, frist, mid, last, p);
        }
    }

    public static void mergeArray(int a[], int frist, int mid, int last,
            int p[]) {
        int i = frist, j = mid;
        int m = mid, n = last;
        int k = 0;
        while (i < m && j < n) {
            if (a[i] <= a[j]) {
                p[k++] = a[i++];
            } else {
                p[k++] = a[j++];
            }
        }
        while (j < n) {
            p[k++] = a[j++];
        }
        while (i < m) {
            p[k++] = a[i++];
        }
        for (i = 0; i < k; i++) {
            a[frist + i] = p[i];
        }
    }

相关学习链接:http://www.cnblogs.com/jillzhang/archive/2007/09/16/894936.html

http://blog.csdn.net/morewindows/article/details/6678165
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值