zoukankan      html  css  js  c++  java
  • 归并排序

    算法复杂度:O(nlogn);

    也许有很多同学说,原来也学过很多O(n^2)或者O(n^3)的排序算法,有的可能优化一下能到O(n)的时间复杂度,但是在计算机中都是很快的执行完了,没有看出来算法优化的步骤,那么我想说有可能是你当时使用的测试用例太小了,我们可以简单的做一下比较:

    当数据量很大的时候 nlogn的优势将会比n^2越来越大,当n=10^5的时候,nlogn的算法要比n^2的算法快6000倍,那么6000倍是什么概念呢,就是如果我们要处理一个数据集,用nlogn的算法要处理一天的话,用n^2的算法将要处理6020天。这就基本相当于是15年。一个优化改进的算法可能比一个比一个笨的算法速度快了许多,这就是为什么我们要学习算法。

    核心思想:分治。

    下面我们来看归并排序的思路(先讲思路再来具体讲归并的细节):

    归并排序(Merge Sort)

     

     当我们要排序这样一个数组的时候,归并排序法首先将这个数组分成一半。如图:

     

    然后想办法把左边的数组给排序,右边的数组给排序,之后呢再将它们归并起来。当然了当我们对左边的数组和右边的素组进行排序的时候,再分别将左边的数组和右边的数组分成一半,然后对每一个部分先排序,再归并。如图:

     

    对于上面的每一个部分呢,我们依然是先将他们分半,再归并,如图:

     

    分到一定细度的时候,每一个部分就只有一个元素了,那么我们此时不用排序,对他们进行一次简单的归并就好了。如图:

     

    归并到上一个层级之后继续归并,归并到更高的层级,如图:

     

    直至最后归并完成。

     

    那么如何归并呢?我们是否可以用O(n)的算法将两个数组归并到一起形成一个数组呢?如果可以的话,我们将可以用递归的过程来实现整个归并。这是你想起来很简单但是操作起来并不是那么简单的问题。

    归并细节:

    比如有两个已经排序好的数组,如何将他归并成一个数组?

    我们可以开辟一个临时数组来辅助我们的归并。也就是说他比我们插入排序也好,选择排序也好多使用了存储的空间,也就是说他需要o(n)的额外空间来完成这个排序。只不过现在计算机中时间的效率要比空间的效率重要的多。无论是内存也好还是硬盘也好可以存储的数据越来越多,所以设计一个算法,时间复杂度是要优先考虑的。

    整体来讲我们要使用三个索引来在数组内进行追踪。

     

    蓝色的箭头表示最终选择的位置,而红色的箭头表示两个数组当前要比较的元素,比如当前是2与1比较,1比2小,所以1放到蓝色的箭头中,蓝色的箭头后移,1的箭头后移。

     

    然后2与4比较,2比4小那么2到蓝色的箭头中,蓝色箭头后移,2后移,继续比较.......

     

    归并思路就是这样了,最后唯一需要注意的是那个先比较完的话,那么剩下的直接不需要比较,把后面的直接移上去就可以了,这个需要提前判定一下。

    归并排序代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    int a[1001],b[1001];
    
    void merge(int arr1[],int l,int mid,int r,int arr2[]){
        int i=l,j=mid+1,k=l;
        while(i<=mid && j<=r){//左右两部分追一比较,记录小的到临时数组 
            if(arr1[i]<arr1[j]){
                arr2[k++] = arr1[i++];
            }else {
                arr2[k++] = arr1[j++];
            }
        }
        //两边数据可能出现不平衡,上面while循环结束可能出现一边元素没放入临时数组情况 
        //左边没放完按顺序放,到此已经拍好序了
        //递归到最后,要么1个元素,要么2个元素,肯定上面while循环已排序或就一个元素加入 
        while(i<=mid){ 
            arr2[k++] = arr1[i++];
        }
        //右边没放完按顺序放,到此已经拍好序了
        while(j<=r){ 
            arr2[k++] = arr1[j++];    
        }
        for(i=l;i<=r;i++){
            arr1[i] = arr2[i];
        }
        cout<<endl;
    }
    
    void mergeSort(int arr1[],int l,int r,int arr2[]){
        //基本情况
        if(l==r) return;
        //原问题分解
        int mid=l+(r-l)/2;
        //解决子问题
        mergeSort(arr1,l,mid,arr2);
        mergeSort(arr1,mid+1,r,arr2);
        //合并子问题 
        merge(arr1,l,mid,r,arr2);
    }
    
    int main(){
        int a[100],n;
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
        mergeSort(a,0,n-1,b);
        for(int i=0;i<n;i++){
            printf("%d ",a[i]);
        }
    }
  • 相关阅读:
    JAVA中的for循环
    Android项目开发全程(三)-- 项目的前期搭建、网络请求封装是怎样实现的
    Android项目开发全程(二)--Afinal用法简单介绍
    Android项目开发全程(一)--创建工程
    Spring中的线程池和定时任务功能
    JVM内存分析
    并发基本概念介绍
    javax顶层接口分析
    HttpServlet源码分析
    SpringMVC创建HelloWorld程序
  • 原文地址:https://www.cnblogs.com/fangzm/p/14145625.html
Copyright © 2011-2022 走看看