合并两个有序数组
题目来源:力扣(LeetCode)
题目详情:
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,8,9,15,0,0,0,0,0], m = 4
nums2 = [2,5,6,12,18], n = 5
输出:
[1,2,5,6,8,9,12,15,18]
解法一:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int[] result = new int[m+n]; // 创建一个大小为m+n的数组,确保装的下所有排序的元素
int i = 0;
int j = 0;
int k = 0;
while(i < m && j < n){ // 从左往右,往result中装数据
if(nums1[i] <= nums2[j]){
result[k++] = nums1[i++];
}else{
result[k++] = nums2[j++];
}
}
while(i < m){ // 将nums1中剩余元素加到result后面
result[k++] = nums1[i++];
}
while(j < n){ // 将nums2中剩余元素加到result后面
result[k++] = nums2[j++];
}
for(int s = 0; s < result.length; s++){ // 将排好序的result拷贝给nums1
nums1[s] = result[s];
}
}
}
这种解法很简单,也很容易理解。就是额外创建一个数组result,数组的大小为m + n即所有排序元素的个数,用来往里面从左到右、从小到大装数据。创建三个指针,i 指向数组nums1的第一个元素,j 指向数组nums2的第一个元素,k 指向数组result的第一个位置。
比较nums1和nums2的指针所指向的当前元素的大小,小的填入到result中 k 指向的位置,此时两个指针都向前移动一位,重复当前操作——填数据。
直到nums1和nums2其中一个数组遍历完了,此时另一个未遍历完的数组中剩余的数都大于result中的元素,将这些数依次加到result后面。
到现在,我们得到了一个已经包含nums1和nums2中所有元素的排好序的数组result,但是还没有结束,因为我们所要做的是合并两个数组,也就是将nums2合并到nums1中,最后nums1是一个合并好的、排好序的数组,我们当前的nums1还不是。所以,我们还要更新一下nums1,将result中的元素拷贝到nums1中,至此nums1才是题目要求的样子。
解法二:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int len1 = m - 1;
int len2 = n - 1;
int len = m + n - 1;
while(len1 >= 0 && len2 >= 0) {
nums1[len--] = nums1[len1] > nums2[len2] ? nums1[len1--] : nums2[len2--];
}
// 将nums2数组从下标0位置开始,拷贝到nums1数组中,从下标0位置开始,长度为len2+1
System.arraycopy(nums2, 0, nums1, 0, len2 + 1);
}
}
这种方法相对巧妙一点,没有额外的创建数组,减少了算法的空间复杂度。让我们来看一看这种解法的思路是什么吧。
解法一的思路是从小到大填数,以此相反,这种解法是从大到小填数据,是在数组nums1上从后往前填数,选择大的填。
while循环中,当数组nums2中的元素都被填完后,nums1就排好序了;当nums1中的元素被填完,而nums2中的元素还有一些时,语句 System.arraycopy(nums2, 0, nums1, 0, len2 + 1); 就发挥作用了,会将nums2中剩余的元素加到nums1中,使得nums1合并排序完成。