[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
为输出数组,将nums1
、nums2
数组中的值存储到aux辅助数组中,使用两个指针i
、j
指向左半边开头与右半边开头,从左向右扫描,取左半边与右半边中较小的依次放入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);
}
}
总结
合并数组时可以从后往前复制,这样可以减少移动次数