题目描述:
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
- 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
- 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
本题主要有三种思路,推荐是使用第三种思路,时间复杂度低,效率高,代码量少。
第一种思路:也就是大多数人的思路,就是排序,重点就在于排序,将第二个数组直接添加到第一个数组后边然后遍历即可。(这道题本人用的排序方法是内设置函数Array.sort,在前面博客中,我分别用了冒泡排序,选择排序,有兴趣可以看我前面的博客),这种思路不是重点,所以不必深究。
第二种思路:就是定义一个额外的数组来保存nums1与nums2比较后的结果,这个数组的长度等于nums1+nums2.当nums1的元素小于nums2的元素时,将nums1的元素保存到新建的数组中,同时让nums1和新建的数组同时向后移动(++),此时注意nums2的下标没有变化,再次比较,要是nums2的元素小于nums移动后的元素,则将nums2的元素保存到新建数组中,然后让nums2与新建数组下标向后移动,nums1下标不变。(也就是nums1,nums2中小的那个元素赋值给新建数组,然后移动这个小元素对应的下标,再判断大小,在移动)
第三种思路: 第二中思路就是前插法,也很明确,但是开辟了新的空间,那我们可以像前一篇博客一样,将nums1看作两个数组,一个数组用来比较,一个数组用来保存。然后发现,还用前插法的话,得不到我们想要的结果,因为影响了原数组中未处理的值,因为nums1除了已有的元素还有额外空间,那我们就用尾插法,这样不会影响原数组未作处理的值。还能很快的得到我们想要的结果。比较方式和第二种方法一样,不过是比较最大值(从尾部插入,因为是排序好的数组,尾部是最大值),将两者中的最大值插入到“新数组的末尾”,逐个比较。
具体看代码:
第一种思路:
// 第一种方法,将第二个数组元素添加到第一个数组元素中,然后排序,重点在排序这个地方,(我用的内置排序方法,其他,归并快速,冒泡选择都行)
int m=3;
int n=3;
int nums1[] = new int[]{1,2,3,0,0,0};
int nums2[] = new int[]{2,5,6};
// 将排序数组nums2赋值到nums1,m个元素后面,然后排序就行,冒泡。选择,快速,我用的是内置函数。
int j=0;
for (int i=m;i<m+n;i++){
nums1[i]=nums2[j];
j++;
}
Arrays.sort ( nums1 );
for (int num:nums1){
System.out.print(num+" ");
}
执行结果:
执行用时:
第二种思路:
//第二种方法,定义三个数组,一个空数组,空数组的大小等于另外两个数组大小的和(能放下两个数组的合并结果)。用来存放另外两个数组(排序后的数组)比较的结果。
// 当第一个数组中的元素小于第二个数组中的元素时(谁小证明谁是当前最小的元素),将第一个数组中对应元素赋值给空数组,然后让第一个数组和空数组加一,向后移动,当第一个数组大于于第二个数组时,将第二个数组中的对应元素赋值给空数组,然后让第二个数组和空数组增一,向后移动。
int nums[] = new int[]{0,0,0,0,0,0,0,0};
int nums1[] = new int[]{1,2,5,7,8,9};
int nums2[] = new int[]{3,5,6};
// 代表操作第一个数组的指针
int j=0;
// 代表操作第二个数组的指针
int k=0;
for (int i=0;i<nums.length;i++){
// 这句话防止数组越界
if (j<nums1.length&&k<nums2.length) {
// 当第一个数组小于等于第二个数组元素时,将第一个数组赋值给空数组,并向后移动。
if (nums1[j] <=nums2[k] ) {
nums[i]=nums1[j];
j++;
// 当第一个数组大于于等于第二个数组元素时,将第二个数组赋值给空数组,并向后移动。
}else if (nums1[j]>nums2[k]){
nums[i]=nums2[k];
k++;
}
}
// 如果某一个数组比较完了,也就是k,j>数组长度时,直接将将另外一个数组中的元素(未比较完)赋值给空数组中剩与的位置。
else if(j>=nums1.length){
nums[i]=nums2[k];
k++;
j++;
}else{
nums[i]=nums1[j];
j++;
k++;
}
}
for (int num:nums){
System.out.print(num+" ");
}
执行结果:
执行用时:
第三种思路:
//第三种方法,(尾插法)不另外开辟空间,将第一个数组看作两个数组,然后进行操作
int nums1[]=new int[]{1,2,5,6,7,0,0,0};
int nums2[]=new int[]{3,8,9};
// n1代表数组1中元素(存在元素,0不算)末尾的下标。
int n1=nums1.length-nums2.length-1;
// n2代表数组二中末尾的下标
int n2=nums2.length-1;
// 尾插法,i从“新数组”(将nums1看成两个数组)的末尾下标,然后比较将元素从末尾插入到新数组中
for (int i=nums1.length - 1; i >=0; i--) {
// n1+1代表nums1中元素的个数,当nums1中的元素的个数为0时,就将nums2中的元素从末尾依次插入新数组(nums1额外空间)中
if ((n1+1)==0){
nums1[i]=nums2[n2];
n2--;
}
// n2+1代表nums2的元素个数,为0时跳出,然后剩与元素就为nums1中的元素
else if ((n2+1) == 0) {
break;
}
// 判断,当nums1末尾元素大于nums2末尾元素时,则将nums1添加到新数组(新数组的空间等于nums1+nums2)的末尾;否则将nums2添加到新数组的末尾,然后逐个递减进行判断。
else if (nums1[n1] >nums2[n2]) {
nums1[i]=nums1[n1];
n1--;
}
else {
nums1[i]=nums2[n2];
n2--;
}
}
for (int num : nums1) {
System.out.print ( num + " " );
}
}
执行结果:
执行用时:
总结:本题表面看起来没有多难,但是其中包含的很多东西值得慢慢体会。尾插法是一个很重要的方法,当前插法会改变原数组元素(将一个数组看成两个)时,尾插法就体现了很大的作用。我还差的远,还得慢慢来。
2019-3-4