题目
一、题目分析
该题目的母题是将两个有序递增数组进行合并,要求合并后仍为有序递增数组,且将排好序的数组放入第三个空数组中。本题目与母体解法类似,甚至有更多的解法:
二、解题思路及代码
1.覆盖0后排序
可以将nums2中的内容覆盖nums1数组后面0的位置后进行排序使其有序递增。
int cmp(int* a, int* b) {
return *a - *b;
}
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
for (int i = 0; i != n; ++i) {
nums1[m + i] = nums2[i];
}
qsort(nums1, nums1Size, sizeof(int), cmp);
}
虽然这种方法直观易解,但这种方法会使得时间复杂度与空间复杂度上升。
时间复杂度:O((m+n)log(m+n))。 排序序列长度为 m+n,套用快速排序的时间复杂度即可,平均情况为 O((m+n)log(m+n))。
空间复杂度:O(log(m+n))。 排序序列长度为 m+n,套用快速排序的空间复杂度即可,平均情况为 O(log(m+n))。
2.创建新数组排序后拷贝
用指针指向一个新数组后用母题的思路将排序好的数字放入新创建的数组,之后拷贝到nums1中即可。
代码如下(示例):
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int* temp=(int*) malloc (sizeof(int)*(m+n));
int i=0,j=0;
int k=0;
while(i<m&&j<n){
if(nums1[i]<nums2[j])
{
temp[k++]=nums1[i++];
}
else
{
temp[k++]=nums2[j++];
}
}
while(i<m){
temp[k++]=nums1[i++];
}
while(j<n){
temp[k++]=nums2[j++];
}
memcpy(nums1,temp,sizeof(int)*(m+n)); //memcpy(a,b,n):把数组b前n个字符拷贝到a数组中去
free(temp);
temp=NULL;
}
时间复杂度:O(m+n)。 指针移动单调递增,最多移动 m+n 次,因此时间复杂度为 O(m+n)。
空间复杂度:O(m+n)。 需要建立长度为 m+n 的中间数组
3.逆向双指针
nums1的后半部分是空的,可以直接覆盖而不会影响结果。因此可以指针设置为从后向前遍历,每次取两者之中的较大者放进num 的最后面。严格来说,在此遍历过程中的意一个时刻,nums1 数组中有m-p1-1个素被放入 nums1的后半部.nums2数组中有n-p2-1个元素被放入 nums1 的后半部,而在指针p1 的后面,nums1 数组有m+n-p1-1个位置。由于m+n-p1-1>m-p1-1+n-p2-1等价于p2>=1永远成立,因此 p1后面的位置永远足够容纳被插入的元素,不会产生 p1的元素被覆盖的情况。
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
int p1 = m - 1, p2 = n - 1;
int tail = m + n - 1;
int cur;
while (p1 >= 0 || p2 >= 0) {
if (p1 == -1) {
cur = nums2[p2--];
} else if (p2 == -1) {
cur = nums1[p1--];
} else if (nums1[p1] > nums2[p2]) {
cur = nums1[p1--];
} else {
cur = nums2[p2--];
}
nums1[tail--] = cur;
}
}
时间复杂度:O(m+n)。 指针移动单调递减,最多移动 m+n次,因此时间复杂度为 O(m+n)。
空间复杂度:O(1)。 直接对数组 原地修改,不需要额外空间。
以上是解决此问题的三种方法,希望对你有帮助。