题目描述
给定两个大小为 m
和 n
的正序(从小到大)数组 nums1
和 nums2
。请你找出这两个正序数组的中位数,并且要求算法的时间复杂度是
O
(
l
o
g
(
m
+
n
)
)
O(log(m+n))
O(log(m+n))。
示例
输入: nums1 = [1, 3], nums2 = [2]
输出: 2.0
解释: 合并数组后排序为 [1, 2, 3],中位数是 2。
输入: nums1 = [1, 2], nums2 = [3, 4]
输出: 2.5
解释: 合并数组后排序为 [1, 2, 3, 4],中位数是 (2 + 3) / 2 = 2.5。
解法一:合并排序
思路:
要求算法时间复杂度为 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n)),不能使用简单的合并排序方法。可以使用归并排序的思想进行解决。
具体过程如下:
- 定义两个指针
p1
和p2
,分别指向数组nums1
和nums2
的起始位置。 - 判断两个指针所指向的元素大小,将小的元素加入到一个临时数组
temp
中,并将对应指针右移一位。 - 重复步骤 2,直到其中一个指针到达数组末尾。
- 判断剩余数组中的元素,并将剩余元素加入到
temp
数组中。 - 如果
temp
数组的长度为奇数,返回temp
数组中的中间元素;如果为偶数,返回中间两个元素的平均值。
时间复杂度为
O
(
m
+
n
)
O(m+n)
O(m+n),其中 m
和 n
分别为 nums1
和 nums2
的长度。合并两个数组的过程需要遍历全部元素。
代码:(合并排序,时间复杂度 O ( m + n ) O(m+n) O(m+n))
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int[] temp = new int[m + n];
int p1 = 0, p2 = 0, p = 0;
while (p1 < m && p2 < n) {
temp[p++] = nums1[p1] < nums2[p2] ? nums1[p1++] : nums2[p2++];
}
while (p1 < m) {
temp[p++] = nums1[p1++];
}
while (p2 < n) {
temp[p++] = nums2[p2++];
}
if ((m + n) % 2 == 0) {
return (temp[(m + n) / 2 - 1] + temp[(m + n) / 2]) / 2.0;
} else {
return temp[(m + n) / 2] * 1.0;
}
}
}
解法二:二分查找
思路:
要求算法时间复杂度为 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n)),可以考虑使用二分查找的思想进行解决。
对于两个有序数组的中位数而言,可以将其转化为寻找第 k
小的数的问题。具体过程如下:
- 定义两个指针
p1
和p2
,分别指向数组nums1
和nums2
的起始位置。 - 根据
k
的值,确定两个指针应该移动的步数。将k
折半,然后分别比较nums1[p1 + newP1 - 1]
和nums2[p2 + newP2 - 1]
的大小:- 如果
nums1[p1 + newP1 - 1]
小于nums2[p2 + newP2 - 1]
,则在前newP1
个元素中必然包含第k
小的数。 - 如果
nums1[p1 + newP1 - 1]
大于等于nums2[p2 + newP2 - 1]
,则在前newP2
个元素中必然包含第k
小的数。 - 如果
nums1[p1 + newP1 - 1]
等于nums2[p2 + newP2 - 1]
,则nums1[p1 + newP1 - 1]
或nums2[p2 + newP2 - 1]
即为第k
小的数。
- 如果
- 根据步数确定指针移动的方向,并将指针往前移动相应的步数。
- 重复步骤 2 和步骤 3,直到
k
缩小到1
。 - 返回当前指针所指位置的元素,即为第
k
小的数。
时间复杂度为
O
(
l
o
g
(
m
+
n
)
)
O(log(m+n))
O(log(m+n)),其中 m
和 n
分别为 nums1
和 nums2
的长度。每次查找过程中, k
的值都被折半,因此需要进行
l
o
g
(
m
+
n
)
log(m+n)
log(m+n) 次。
代码:(二分查找,时间复杂度 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n)))
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int total = m + n;
if (total % 2 == 0) {
int left = findKth(nums1, 0, nums2, 0, total / 2);
int right = findKth(nums1, 0, nums2, 0, total / 2 + 1);
return (left + right) / 2.0;
} else {
return findKth(nums1, 0, nums2, 0, total / 2 + 1);
}
}
private int findKth(int[] nums1, int start1, int[] nums2, int start2, int k) {
if (start1 >= nums1.length) {
return nums2[start2 + k - 1];
}
if (start2 >= nums2.length) {
return nums1[start1 + k - 1];
}
if (k == 1) {
return Math.min(nums1[start1], nums2[start2]);
}
int mid1 = start1 + k / 2 - 1 < nums1.length ? nums1[start1 + k / 2 - 1] : Integer.MAX_VALUE;
int mid2 = start2 + k / 2 - 1 < nums2.length ? nums2[start2 + k / 2 - 1] : Integer.MAX_VALUE;
if (mid1 < mid2) {
return findKth(nums1, start1 + k / 2, nums2, start2, k - k / 2);
} else {
return findKth(nums1, start1, nums2, start2 + k / 2, k - k / 2);
}
}
}