【leetcode】88. 合并两个有序数组
题目内容
思路分析
方法一:插入排序
看到本题,自己想的解法是进行插入排序。
与正常的插入排序一样,都是二分查找+插入的思路。只不过本题有一点点不同:数组一中用0预留出了数组二的位置,因此
- 数组一整体相当于不是有序的,如[1, 2, 3, 0, 0, 0]。不可以直接在
begin()到end()的范围内使用lower_bound()函数。 - 数组的空间是有限的,因此不能直接使用
insert()函数,否则会扩大数组一的空间,数组一中的0也无法处理。
为解决上面两个问题
- 使用一个全局变量
nums1_size来存储nums1中有意义数字(非占位符数字)的个数,则lower_bound的范围为begin()到begin() + nums1_size。 - 自己编写插入函数,思路为:将插入位置
position到nums1_size-1下标处的所有数字都往后挪一格,再将要插入的数放置于position处
方法二:合并后排序
可以将数组二直接加到数组一里,然后执行排序算法即可。
方法三:双指针
由于两个数组都是有序的,因此可以新开辟一个大小为m+n的数组result。然后对比两个要排序的数组的第一个元素,谁小就将谁加入到result数组的末尾;另外当一个数组的元素全部插入完毕后,后续直接插入另一个数组的剩余元素。
方法四:逆向双指针
方法三中新开辟了一个大小为m+n的数组,比较蠢。
观察可知,nums1的后面有一部分是空的,因此可以指针从后向前遍历,选取两个数组中较大的值放在nums1的后面,即可在不开辟额外空间的情况下排序。
代码
#include <vector>
#include <math.h>
#include <limits.h>
using namespace std;
// 插入排序
// 时间复杂度O(n^2)
// 空间复杂度O(1)
class Solution1 {
public:
int nums1_size; // nums1中有意义的元素个数
void my_insert(vector<int>& nums, int position, int data) {
// 挪元素:从最后一个有意义的元素处开始倒序直到position处,各往后挪一格
// 为position处插入元素做准备
for (auto i = nums1_size - 1; i >= position; --i) {
nums[i + 1] = nums[i];
}
nums[position] = data;
nums1_size++;
}
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
nums1_size = m;
for (int num2 : nums2) {
auto insert_position = lower_bound(nums1.begin(),
nums1.begin() + nums1_size, num2) - nums1.begin();
my_insert(nums1, insert_position, num2);
}
}
};
// 合并后排序
// 时间复杂度O( (m+n)log(m+n) )
// 空间复杂度:平均O( log(m+n) ),最坏O(m+n)
class Solution2 {
public:
// 快排(写的时候又忘了...)
void quick_sort(vector<int>& nums) {
quick_sort(nums, 0, nums.size() - 1);
}
void quick_sort(vector<int>& nums, int left, int right) {
if (left >= right) return;
int pivot = nums[left];
int i{ left }, j{ right };
// 外层while控制i与j是否相遇
while (i != j) {
// 右指针需要寻找<=pivot的数
while (i < j && nums[j] > pivot) {
--j;
}
// 左指针需要寻找>pivot的数
while (i < j && nums[i] <= pivot) {
++i;
}
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
nums[left] = nums[i];
nums[i] = pivot;
quick_sort(nums, left, i - 1);
quick_sort(nums, i + 1, right);
}
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
// 合并数组
for (int i = m; i < m + n; ++i) {
nums1[i] = nums2[i - m];
}
// 排序
quick_sort(nums1);
}
};
// 双指针
// 时间复杂度O(m+n)
// 空间复杂度O(m+n)
class Solution3 {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
vector<int> result;
int cur, p1{ -1 }, p2{ -1 };
while(p1 < m - 1 || p2 < n - 1) {
if (p1 == m - 1) {
cur = nums2[++p2];
} else if (p2 == n - 1) {
cur = nums1[++p1];
} else if (nums1[p1 + 1] <= nums2[p2 + 1]) {
cur = nums1[++p1];
} else {
cur = nums2[++p2];
}
result.push_back(cur);
}
copy(result.begin(), result.end(), nums1.begin());
}
};
// 逆向双指针
// 时间复杂度O(m+n)
// 空间复杂度O(1)
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int cur, p1{m}, p2{n}, insert_position{m + n - 1};
while(p1 != 0 || p2 != 0) {
if (p1 == 0) {
cur = nums2[--p2];
} else if (p2 == 0) {
cur = nums1[--p1];
} else if (nums1[p1 - 1] >= nums2[p2 - 1]) {
cur = nums1[--p1];
} else {
cur = nums2[--p2];
}
nums1[insert_position] = cur;
insert_position--;
}
}
};
int main() {
Solution s;
vector<int> nums1{1,2,3,0,0,0}, nums2{2,5,6};
int m{3}, n{3};
s.merge(nums1, m, nums2, n);
int a = 0;
}
收获
- 插入排序这种还是得多练,之前写了两遍又忘了
- 一题多解还是很有必要的