合并数组
给你两个按 非递减顺序 排列的整数数组 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和nums2的开始,比较指针所指的两个数,将较小的数添加到一个新的数组中,然后移动该指针。当一个数组被遍历完后,将另一个数组剩下的元素全部添加到新的数组中。最后再将新数组的元素复制回nums1。这个方法的时间复杂度是O(m+n),但是需要O(m+n)的额外空间。
双指针从后往前:为了避免使用额外的空间,我们可以从nums1和nums2的末尾开始,使用两个指针从后往前进行比较,并将较大的数从后往前填入nums1。这种方法同样的时间复杂度是O(m+n),但是不需要额外的空间。
归并排序的合并步骤:这题其实可以看做是归并排序中的合并步骤。我们可以使用归并排序合并两个有序数组的方法,也是通过双指针实现。其中,双指针从后往前的方法是最优的,因为它既满足了O(m+n)的时间复杂度,又没有使用额外的空间。
第一种解法:双指针从前往后
实现思路:
- 使用两个指针
p1
和p2
分别初始化为nums1
和nums2
的起始位置。 - 比较两个指针所指的元素,将较小的元素添加到一个新的数组中。
- 移动指向较小元素的指针。
- 当一个数组的元素被遍历完后,将另一个数组中的剩余元素全部添加到新的数组中。
- 最后,将新数组的元素复制到
nums1
中。
时间复杂度分析:
- 在最坏的情况下,两个指针都会遍历其对应的数组一次,因此时间复杂度为O(m+n)。
空间复杂度分析:
- 需要一个额外的数组存储合并后的结果,该数组的长度为m+n,因此空间复杂度为O(m+n)。
代码实现:
public void merge(int[] nums1, int m, int[] nums2, int n) {
int[] merged = new int[m + n]; // 创建一个新数组用于存储合并后的结果
int p1 = 0; // 指向nums1的起始位置
int p2 = 0; // 指向nums2的起始位置
int p = 0; // 指向merged的起始位置
// 当两个数组都还有元素时,进行比较和合并
while (p1 < m && p2 < n) {
// 将较小的元素添加到merged中,并移动对应的指针
if (nums1[p1] < nums2[p2]) {
merged[p] = nums1[p1];
p1++;
} else {
merged[p] = nums2[p2];
p2++;
}
p++;
}
// 如果nums1还有剩余元素,将其全部添加到merged中
while (p1 < m) {
merged[p] = nums1[p1];
p1++;
p++;
}
// 如果nums2还有剩余元素,将其全部添加到merged中
while (p2 < n) {
merged[p] = nums2[p2];
p2++;
p++;
}
// 将merged的元素复制回nums1
for (int i = 0; i < m + n; i++) {
nums1[i] = merged[i];
}
}
第二种解法:双指针从后往前
实现思路:
- 使用两个指针
p1
和p2
,分别初始化为nums1
和nums2
的有效元素的末尾位置。 - 设置一个指针
tail
,指向nums1
数组的末尾。 - 从后往前比较
p1
和p2
指向的元素,将较大的元素放到tail
所指的位置。 - 移动较大元素对应的指针,并将
tail
指针前移。 - 重复上述过程,直至
p1
或p2
任一指针移动到数组起始位置之前。 - 如果
nums2
数组还有元素未被考虑,将其剩余元素复制到nums1
的前部。
时间复杂度分析:
p1
和p2
两个指针最多会遍历其对应的数组一次,所以时间复杂度为O(m+n)。
空间复杂度分析:
- 本方法没有使用额外的存储空间,只用了几个辅助指针变量,因此空间复杂度为O(1)。
代码实现:
public void merge(int[] nums1, int m, int[] nums2, int n) {
int p1 = m - 1; // 指向nums1有效部分的末尾
int p2 = n - 1; // 指向nums2的末尾
int tail = m + n - 1; // 指向nums1的末尾,用于从后往前填充元素
// 当两个数组都还有元素时,进行比较和合并
while (p1 >= 0 && p2 >= 0) {
// 将较大的元素填充到nums1的末尾,并移动对应的指针
if (nums1[p1] > nums2[p2]) {
nums1[tail] = nums1[p1];
p1--;
} else {
nums1[tail] = nums2[p2];
p2--;
}
tail--;
}
// 如果nums2还有剩余元素,直接复制到nums1的前部
// 因为p1指针已经移动到了-1的位置,所以只需考虑p2即可
while (p2 >= 0) {
nums1[tail] = nums2[p2];
p2--;
tail--;
}
}
第三种解法:归并排序的合并步骤
事实上,第一种解法本质上就是归并排序中的合并步骤。归并排序的核心操作是将两个已排序的数组(或子数组)合并成一个排序数组,这与我们当前的问题非常相似。
实现思路:
- 使用两个指针
p1
和p2
,分别初始化为nums1
和nums2
的起始位置。 - 使用一个额外的数组
merged
,保存合并后的结果。 - 比较
p1
和p2
所指的元素,将较小的元素放到merged
中,并移动对应的指针。 - 当一个数组被遍历完后,将另一个数组剩余的元素全部放入
merged
中。 - 最后,将
merged
数组的内容复制到nums1
中。
时间复杂度分析:
p1
和p2
两个指针最多会遍历其对应的数组一次,所以时间复杂度为O(m+n)。
空间复杂度分析:
- 由于使用了一个额外的
merged
数组来保存结果,所以空间复杂度为O(m+n)。
代码实现:
public void merge(int[] nums1, int m, int[] nums2, int n) {
int[] merged = new int[m + n]; // 创建一个新数组用于存储合并后的结果
int p1 = 0; // 指向nums1的起始位置
int p2 = 0; // 指向nums2的起始位置
int p = 0; // 指向merged的起始位置
// 当两个数组都还有元素时,进行比较和合并
while (p1 < m && p2 < n) {
// 将较小的元素添加到merged中,并移动对应的指针
if (nums1[p1] <= nums2[p2]) {
merged[p] = nums1[p1];
p1++;
} else {
merged[p] = nums2[p2];
p2++;
}
p++;
}
// 如果nums1还有剩余元素,将其全部添加到merged中
while (p1 < m) {
merged[p] = nums1[p1];
p1++;
p++;
}
// 如果nums2还有剩余元素,将其全部添加到merged中
while (p2 < n) {
merged[p] = nums2[p2];
p2++;
p++;
}
// 将merged的元素复制回nums1
for (int i = 0; i < m + n; i++) {
nums1[i] = merged[i];
}
}
最近很多小伙伴,让我帮忙找一套 Java 学习资料,于是我翻遍了收藏的 1024G 资料,找到一套华为工程师总结的 Java 笔记,可以说是 Java 程序员必备!
整个资料包内容专注 Java 技术,包括 Spring、Spring Boot/Cloud、Dubbo、JVM、集合、多线程、JPA、MyBatis、MySQL、大数据、Nginx、Git、Docker、GitHub、Servlet、JavaWeb、IDEA、Redis、算法、面试题等几乎覆盖了 Java 基础和进阶的方方面面,非常适合初学者入门和进阶者巩固知识!
据说已经有小伙伴通过这套资料,成功的入职了蚂蚁金服、今日头条等大厂。而且,这些资料不是扫描版的,里面的文字都可以直接复制,非常便于我们学习!
分享在这里~ 免积分直接下载~
https://download.csdn.net/download/weixin_42116348/88439145