zoukankan      html  css  js  c++  java
  • leetcode 88 Merge Sorted Array 归并排序

    归并排序:先将数组一分为二,将左边部分排序(同样将其一分为二),再将右边部分排序,最后逐层归并。(分治策略)(稳定排序)。

    算法稳定性 -- 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!

    先排序的时间复杂度为log(n);

    后归并的时间复杂度为n;

    总的时间复杂度nlog(n)。

     

    1)自顶向下归并排序的代码:需要一个和待排序数组相同的空间,故将arr[]拷贝一份给aux[]

    //将arr[l...mid]和 arr[mid+1...r]两部分进行归并
    template<typename T>
    void merge(T arr[], int l, int mid,  int r){
        //需要一个临时的和arr同样大小的空间
        T aux[r-l+1];
        for(int i=l;i<=r;i++)
            //将arr复制给aus,aus下标从0开始,而arr下标从l开始
            aux[i-l] = arr[i];
    //初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
    int i = l, j = mid+1; for(int k=l;k<=r;k++){ //逐步比较左部分的第i个元素和右部分的第j个元素的大小 //首先判断下标i和j的合法性 if(i>mid){ //左边已经遍历完,但右边还有 arr[k] = aux[j-l]; j++ } else if(j>r){ //右边已经遍历完,将左边的元素给arr arr[k] = aux[i-l]; i++; } else if(aux[i-l]<aux[j-l]){ arr[k] = aux[i-l]; i++; } else{ arr[k] = aux[j-l]; j++; } } } //递归使用归并排序,对arr[l...r]的范围进行排序 template<typename T> void mergeSort(T arr[], int l, int r){ if(l>=r) return; int mid = (l+r)/2; mergeSort(arr, l, mid); mergeSort(arr, mid+1,r); //优化:只有当 mid>mid+1 时才需要对左右两边进行排序 //因为左边或右边本身是有序的,如果 mid<=mid+1 则不需要对其归并排序了 if(arr[mid] > arr[mid+1]) merge(arr,l,mid,r); }

     当数组中的元素足够少时,可以将递归出口改为插入排序,虽然插入排序的时间复杂度是O(n),但是可以提高效率。

     2)自底向上归并排序:

    template<typename T>      //泛型
    void mergeSortBU(T arr[], int n){
        //自底向上归并
        //对merge的元素个数进行遍历:1,2,4,8以此类推
        for(int sz=1 ; sz<=n ; sz+=sz ){
            for(int i=0; i+sz<n; i+=sz+sz)
                //对arr[i...i+sz-1]和arr[i+sz...i+2*sz-1]进行归并
                merge(arr, i, i+sz-1, min(i+sz+sz-1, n-1));
        }    
    }

     

    注意:nums1和nums2 是有序的,m代表nums1元素的个数。

    这里的解题思想是归并排序的merge()函数的思想,先开辟一个与nums1相同大小和值的空间aux,再将其与nums2逐一对比,用小的来替换nums1的值。

    class Solution {
    public:
        void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
            
            if (m <= 0 && n <= 0) return;
            //int aux[m];
            vector<int> aux(m);
            for(int i=0;i<m;i++){
                //将nums1的值拷贝给aux
                aux[i] = nums1[i];
            }
            
            int i=0,j=0;
            for(int k=0;k<nums1.size();k++){
                if(i>m-1){
                    //aux数组超界
                    nums1[k] = nums2[j];
                    j++;
                }
                else if(j>n-1){
                    nums1[k] = aux[i];
                    i++;
                }    
                else if(nums2[j]<aux[i]){
                    nums1[k] = nums2[j];
                    j++;
                }
                else{
                    nums1[k] = aux[i];
                    i++;
                }
                    
            }
        }
    };

    解法二:从两个数组的末尾开始比较大小,从下标为m+n-1开始存放,将大的存放在nums1的末尾。如果最后剩下的是nums1,则不需要移动;若是nums2中的元素则需要放在nums1的相应位置。

    class Solution {
    public:
        void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
            
            if (m <= 0 && n <= 0) return;
            
            int count = m+n-1;
            --m, --n;   //m和n是长度,所以要减1变成下标
            while(m>=0 && n>=0){
                if(nums1[m] >nums2[n])
                    nums1[count--] = nums1[m--];
                else
                    nums1[count--] = nums2[n--];
            }    
            while(n>=0){
               //当m已经全部遍历完,n还剩下
                nums1[count--] = nums2[n--];
            }
        }
    };
  • 相关阅读:
    Net中应用 Redis 扩展类
    【ASP.Net MVC3 】使用Unity 实现依赖注入
    NET平台常用的框架整理
    ABP 基于DDD的.NET开发框架 学习(三)创建模块:任务管理
    ABP 基于DDD的.NET开发框架 学习(二)创建实体
    ABP 基于DDD的.NET开发框架 学习(一)
    生成二维码图片
    淘宝图片指纹匹配功能c#实现
    webAPI中“System.Web.Http.HttpConfiguration”不包含“EnableSystemDiagnosticsTracing”的定义解决办法
    NuGet包 安装相关指令
  • 原文地址:https://www.cnblogs.com/Bella2017/p/10126088.html
Copyright © 2011-2022 走看看