今日在刷算法题的过程中看到了一道比较有意思的题目。故此分享,亦作笔记之用。
那就赶快来看看该题:88. 合并两个有序数组 - 力扣(LeetCode)

按照题目要求是是让我们合并nums1和nums2俩个有序数组到nums1中。首先可以想到的最为简单粗暴的解法便是将nums2的所有数据都放到nums1中的无效位置(下标[m, m + n - 1]的区间),再使用排序算法对nums1进行排序。
固然上述方法是可以解决,但不够优雅,并且题目中存在一个进阶要求------时间复杂度O(m + n)。而上一个方法的时间复杂度至少也是O(NlogN)。此时,通过前面提到的排序与标题中的归并,我们不难想到一个可以满足该要求的方法:即创建一个拷贝数组tmp,使用归并的思想将nums1与nums2两个有序数组放到tmp,最后再将tmp拷贝回nums1中即可。
上述方法确实可行,但这样也无法称之为优雅。此时我们会想着如果不使用这个拷贝数组tmp,让空间复杂度降到O(1)便能进行合并就好了。接下来便是这道题的巧妙之处:
我们可以发现nums1的数组长度是m + n,无效数据全部存储再前m位置,后面存在n个无效数据的长度。我们是否可以将合并的方式反过来,即从后往前进行归并,将两数组中最大的数据先放到末尾位置,即:
通过逆序的方法进行合共两数组,从两数组有效数据的末尾位置开始从后往前进行合并,从末尾位置开始,每次合并从后往前将比较大的数放到nums1的数组中。
此时可能会考虑一个问题,nums2中的数据会不会影响到nums1中的有效数据?答案是否定的。
这是由于nums1中无效数据的长度为n,即使nums1所有的数据均小于nums2,也仅到下标m的位置,若存在大于nums2的数据,会从后往前缩小nums1的有效数据长度,最终也会是刚好在有效数据的下个位置停下,所以合并过程中是肯定不会影响到nums1中的有效数据的。
以下是题解代码:
cpp
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = m - 1, j = n - 1, k = m + n - 1;
while (i >= 0 && j >= 0)
{
if (nums1[i] > nums2[j])
nums1[k] = nums1[i--];
else
nums1[k] = nums2[j--];
--k;
}
while (j >= 0)
nums1[k--] = nums2[j--];
}
};