[leetCode]88.合并两个有序数组

在这里插入图片描述

解法一 插入排序

思路:因为两个数组是有序的,因此可以在nums1尾部插入一个元素,再对左半部分进行扫描将插入的元素放在合适位置。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        for(int i = m; i < m+n; i++) {
            nums1[i] = nums2[i-m];
            for(int j = i; j > 0 && nums1[j] < nums1[j-1]; j--){
                exch(nums1,j,j-1);
            }
        }
    }

    public void exch(int[] a, int i, int j){
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

解法二 双指针

思路:由于是归并两个有序数组直接就可以想到归并排序中的归并操作,nums1为输出数组,将nums1nums2数组中的值存储到aux辅助数组中,使用两个指针ij指向左半边开头与右半边开头,从左向右扫描,取左半边与右半边中较小的依次放入nums1中。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int[] aux = new int[m+n];//辅助数组
        for(int i =  0; i < m + n; i++)
            aux[i] = i < m? nums1[i]: nums2[i-m];
        int i = 0, j = m, k = 0;
        while(k < m+n){
            if(i==m) nums1[k] = aux[j++];//左边取完用右边
            else if(j==m+n) nums1[k] = aux[i++];//右边取完用左边
            else if(aux[i] < aux[j]) nums1[k] = aux[i++];//左边小于右边用左边
            else nums1[k] = aux[j++];//右边小于左边用右边
            k++;
        }
    }
}

解法三 二分查找

思路:每次从nums2取出一个元素,通过二分查找查找该元素在nums1中的插入位置,将该位置及之后原有的的元素向后移动一位,给新元素腾位置,再将新元素插入。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        for(int i = m; i < m+n; i++){
            int lo = 0, hi = i - 1;
            while(lo <= hi) {
                int mid = lo + (hi - lo)/2;
                if(nums1[mid] <= nums2[i-m]) lo = mid + 1;
                else if (nums1[mid] > nums2[i-m]) hi = mid - 1;
            }
            for(int j = i; j > lo; j--){
                nums1[j] = nums1[j - 1];
            }
            nums1[lo] = nums2[i-m];  
        }
    }

    public void exch(int[] a, int i, int j){
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

上面代码中空间复制度为O(m+n)可以继续优化,只复制nums1中的元素到一个新数组中

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int[] nums1_copy = new int[m];
        System.arraycopy(nums1, 0, nums1_copy, 0, m);
        int i = 0, j = 0, k = 0;
        while(i < m && j < n){
            nums1[k++] = nums1_copy[i] < nums2[j] ? nums1_copy[i++]:nums2[j++];
        }
        if(i < m)  System.arraycopy(nums1_copy, i, nums1, i+j, m+n-i-j);
        if(j < n)  System.arraycopy(nums2, j, nums1, i+j, m+n-i-j);         
    }
}

上面代码已经达到了最优的时间复杂度O(m+n),空间复杂度优化为了O(m),可以将指针指向数组尾部从后往前遍历,将较大的值放在nums1尾部

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i = m-1, j = n-1, k = m+n-1;
        while(i >= 0 && j >= 0){
            //比较nums1[i]与nums2[j]将较大的放在nums1尾部
            nums1[k--] = nums1[i] < nums2[j] ? nums2[j--]:nums1[i--];
        } 
        //补充nums2缺失的元素,nums1=[0],m=0,nums2=[1],n=1这种情况 
        System.arraycopy(nums2, 0, nums1, 0, j+1); 
    }
}

总结

合并数组时可以从后往前复制,这样可以减少移动次数

posted @ 2020-07-03 11:02  消灭猕猴桃  阅读(68)  评论(0编辑  收藏  举报