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));
    }    
}

 leetcode 88 Merge Sorted Array 归并排序

注意: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--];
        }
    }
};