做seo网站的公司哪家好,金华网站建设哪里好,货源网,WordPress表白墙主题本文属于「征服LeetCode」系列文章之一#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁#xff0c;本系列将至少持续到刷完所有无锁题之日为止#xff1b;由于LeetCode还在不断地创建新题#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章… 本文属于「征服LeetCode」系列文章之一这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁本系列将至少持续到刷完所有无锁题之日为止由于LeetCode还在不断地创建新题本系列的终止日期可能是永远。在这一系列刷题文章中我不仅会讲解多种解题思路及其优化还会用多种编程语言实现题解涉及到通用解法时更将归纳总结出相应的算法模板。 为了方便在PC上运行调试、分享代码文件我还建立了相关的仓库https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解还可以一同分享给他人。 由于本系列文章的内容随时可能发生更新变动欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2另有两个整数 m 和 n 分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中使合并后的数组同样按 非递减顺序 排列。
注意 最终合并后数组不应由函数返回而是存储在数组 nums1 中。为了应对这种情况nums1 的初始长度为 m n其中前 m 个元素表示应合并的元素后 n 个元素为 0 应忽略。nums2 的长度为 n 。
示例 1
输入nums1 [1,2,3,0,0,0], m 3, nums2 [2,5,6], n 3
输出[1,2,2,3,5,6]
解释需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] 其中斜体加粗标注的为 nums1 中的元素。示例 2
输入nums1 [1], m 1, nums2 [], n 0
输出[1]
解释需要合并 [1] 和 [] 。
合并结果是 [1] 。示例 3
输入nums1 [0], m 0, nums2 [1], n 1
输出[1]
解释需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意因为 m 0 所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。提示
nums1.length m nnums2.length n0 m, n 2001 m n 200-10^9 nums1[i], nums2[j] 10^9
进阶 你可以设计实现一个时间复杂度为 O(m n) 的算法解决此问题吗 解法1 双指针
为了利用数组 nums 1 \textit{nums}_1 nums1 与 nums 2 \textit{nums}_2 nums2 已被排序的性质可以使用双指针方法。这一方法将两个数组看作队列每次从两个数组头部取出比较小的数字放到结果中。
为了避免覆盖可以先将 n u m s 1 nums_1 nums1 的元素全部移动到后面去将前面空出来。注意为了避免元素覆盖需要从 n u m s 1 nums_1 nums1 的最后一个元素开始移动将它移动到数组的最后。
为两个数组分别设置一个指针 p 1 p_1 p1 与 p 2 p_2 p2 来作为队列的头部指针。代码实现如下
class Solution {
public:void merge(vectorint nums1, int m, vectorint nums2, int n) {int all n m;for (int i all - 1; i all - m; --i) nums1[i] nums1[i - n];int p1 n, p2 0, k 0;while (p1 all p2 n) {if (nums1[p1] nums2[p2]) nums1[k] nums1[p1];else nums1[k] nums2[p2];}while (p1 all) nums1[k] nums1[p1];while (p2 n) nums1[k] nums2[p2];}
}复杂度分析
时间复杂度 O ( m n ) O(mn) O(mn) 。指针移动单调递增最多移动 m n mn mn 次因此时间复杂度为 O ( m n ) O(mn) O(mn) 。空间复杂度 O ( 1 ) O(1) O(1)。 解法2 逆向双指针
方法1中之所以要将 n u m s 1 nums_1 nums1 的元素移动到后面是因为如果直接合并到数组 nums 1 \textit{nums}_1 nums1 中 nums 1 \textit{nums}_1 nums1 中的元素可能会在取出之前被覆盖。那么如何直接避免覆盖 nums 1 \textit{nums}_1 nums1 中的元素呢观察可知 nums 1 \textit{nums}_1 nums1 的后半部分是空的可以直接覆盖而不会影响结果。因此指针设置为从后向前遍历每次取两者之中的较大者放进 nums 1 \textit{nums}_1 nums1 的最后面。
严格来说在此遍历过程中的任意一个时刻 nums 1 \textit{nums}_1 nums1 数组中有 m − p 1 − 1 m-p_1-1 m−p1−1 个元素被放入 nums 1 \textit{nums}_1 nums1 的后半部 nums 2 \textit{nums}_2 nums2 数组中有 n − p 2 − 1 n-p_2-1 n−p2−1 个元素被放入 nums 1 \textit{nums}_1 nums1 的后半部而在指针 p 1 p_1 p1 的后面 nums 1 \textit{nums}_1 nums1 数组有 m n − p 1 − 1 mn-p_1-1 mn−p1−1 个位置。由于 m n − p 1 − 1 ≥ m − p 1 − 1 n − p 2 − 1 mn-p_1-1\geq m-p_1-1n-p_2-1 mn−p1−1≥m−p1−1n−p2−1 等价于 p 2 ≥ − 1 p_2\geq -1 p2≥−1 永远成立因此 p 1 p_1 p1 后面的位置永远足够容纳被插入的元素不会产生 p 1 p_1 p1 的元素被覆盖的情况。
当然不会覆盖的原因一句话就能说明白把 nums 1 \textit{nums}_1 nums1 的数字移到另一个空位又产生了一个新的空位空位个数不变所以总是有空位可以让 nums 2 \textit{nums}_2 nums2 的数字填入。
实现代码如下
class Solution {
public:void merge(vectorint nums1, int m, vectorint nums2, int n) {int p1 m - 1, p2 n - 1, 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 ) O(mn) O(mn)。 指针移动单调递减最多移动 m n mn mn 次因此时间复杂度为 O ( m n ) O(mn) O(mn) 。空间复杂度 O ( 1 ) O(1) O(1) 。 直接对数组 nums 1 \textit{nums}_1 nums1 原地修改不需要额外空间。