题目地址:https://leetcode.cn/problems/merge-sorted-array/
如果对归并排序有印象,其实能直接看出来这个算法就是归并排序“并”的部分(已经限定了数组是非递减排序的)。由于两个数组已经是排序好的,那么可以维护两个指针i和j,分别代表两个数组的索引值,且初始值都为0。依次比较nums1[i]和nums2[j]的大小,小的保存,指针后移并继续比较,直到一个数组遍历完成,将另一个数组的剩余元素“平移”即可。代码如下:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i=0;
int j=0;
int k=0;
int[] result = new int[m+n];
while(i<m && j<n) {
if(nums1[i] <= nums2[j]) {
result[k++] = nums1[i++];
}else{
result[k++] = nums2[j++];
}
}
//合并剩余数组中的元素
if(i == m) {
while(j<n) {
result[k++] = nums2[j++];
}
}
if(j == n) {
while(i<m) {
result[k++] = nums1[i++];
}
}
for (int i1 = 0; i1 < nums1.length; i1++) {
nums1[i1] = result[i1];
}
}
}
时间复杂度分析:上面代码涉及到4个非嵌套循环,因此总的时间复杂度线性的,精确一点就是O(m+n)。
空间复杂度分析:由于引入了一个长度为m+n的临时数组,因此空间复杂度也为O(m+n)
注:上述while循环的写法可以改造为更精简的for循环写法,代码如下:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int[] result = new int[m+n];
for (int i = 0, i1=0, i2=0; i < m+n; i++) {
//如果num1已经取完,直接取nums2
if(i1==m) {
result[i] = nums2[i2++];
}else if(i2==n) {
result[i] = nums1[i1++];
}else if(nums1[i1]<=nums2[i2]) {
result[i] = nums1[i1++];
}else{
result[i] = nums2[i2++];
}
}
for(int i=0;i<m+n;i++) {
nums1[i] = result[i];
}
}
}
紧接着,考虑是否能够原地排序,不引入额外的空间复杂度。观察nums1数组,后面有n个保留给nums元素的位置,如果换一个思路,从后往前遍历,就可以在不引入额外存储的情况下实现。代码如下:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int k = m+n;
for (int i=k-1, i1=m-1, i2=n-1; i >= 0; i--) {
if(i1 < 0) {
nums1[i] = nums2[i2--];
}else if(i2 < 0) {
nums1[i] = nums1[i1--];
}else if(nums1[i1]<=nums2[i2]) {
nums1[i] = nums2[i2--];
}else{
nums1[i] = nums1[i1--];
}
}
}
}
反思:基础的方法很容易都能想到,但是算法就是拼的特定条件下的时间复杂度和空间复杂度最优,要有不满足的精神,将代码优化到极致。另外,本题目从尾到头遍历省去一个额外数组的解法也提醒我,当觉得问题无解的时候,多跳出固化思维,从另外一个视角去看,或许就是“山重水复疑无路,柳暗花明又一村”了。
本文详细解析LeetCode第88题,介绍如何合并两个已排序的数组,通过比较元素并原地操作,达到空间复杂度优化。讨论了不同解法的时间和空间复杂度,并强调算法优化的重要性。
2316

被折叠的 条评论
为什么被折叠?



