4. 寻找两个正序数组的中位数
- 1. 题目描述
- 2.详细题解
- 3.代码实现
- 3.1 Python
- 3.2 Java
1. 题目描述
题目中转:4. 寻找两个正序数组的中位数
2.详细题解
两个有序数组,寻找二者的中位数,最直观的方法是先归并这两个数组为一个有序数组,中位数则为中间的数字,算法时间复杂度为
O
(
l
o
g
(
m
+
n
)
)
O(log(m+n))
O(log(m+n)),具体代码实现见Python方法一。
要求时间复杂度不大于
O
(
l
o
g
(
m
+
n
)
)
O(log (m+n))
O(log(m+n))时间复杂度,首先肯定不能先归并两个数组了,两个数组均有序,应当充分运用这个条件。
思考什么是中位数?通俗的说,即将数据一分为二,且其中一个集合中数据最大值小于等于另一个集合中数据的最小值。
中位数性质已经很明晰。因此,对于题目中的两个数组,相当于要在数组
1
1
1(长度为
m
m
m)和数组
2
2
2(长度为
n
n
n)中分别寻找一个位置
i
i
i和
j
j
j,将两个数组一分为二,两个数组的左半部分构成一个集合,右半部分构成另一个集合。
i
i
i和
j
j
j的取值范围分别为
[
0
,
m
]
[0, m]
[0,m]和
[
0
,
n
]
[0,n]
[0,n],此时左集合和右集合的元素个数应满足如下关系:
i
+
j
=
m
−
i
+
n
−
j
i+j = m -i + n - j
i+j=m−i+n−j 或者
i
+
j
=
m
−
i
+
n
−
j
+
1
i+j = m -i + n - j + 1
i+j=m−i+n−j+1
上述等式分别表示
m
+
n
m+n
m+n为偶数和奇数的情况,推导可得:
i
+
j
=
(
m
+
n
)
/
2
i + j= (m+n)/2
i+j=(m+n)/2或
i
+
j
=
(
m
+
n
+
1
)
/
2
i + j= (m+n+1)/2
i+j=(m+n+1)/2,二者可合并为
i
+
j
=
(
m
+
n
+
1
)
/
2
i + j= (m+n+1)/2
i+j=(m+n+1)/2,仅保留整数结果。
进一步可得
j
=
(
m
+
n
+
1
)
/
2
−
i
j = (m+n+1)/2 - i
j=(m+n+1)/2−i,因为
i
i
i和
j
j
j的取值范围分别为
[
0
,
m
]
[0, m]
[0,m]和
[
0
,
n
]
[0,n]
[0,n],因此
m
<
=
n
m<=n
m<=n时该等式成立,否则当
i
=
m
i=m
i=m时,显然不能均分该两个数组,因此应首先判断两个数组的长度,将长度更小的数组作为第一个数组
接下来进行二分条件分析,对于第一个数组,取中间值为
i
=
(
l
e
f
t
+
r
i
g
h
t
)
/
/
2
i=(left+right)//2
i=(left+right)//2,则左边界值为
n
u
m
s
1
[
i
−
1
]
nums1[i-1]
nums1[i−1],右边界值为
n
u
m
s
1
[
i
]
nums1[i]
nums1[i];随之确定第二个数组的分割为
j
j
j,左边界值为
n
u
m
s
2
[
j
−
1
]
nums2[j-1]
nums2[j−1],右边界值为
n
u
m
s
2
[
j
]
nums2[j]
nums2[j],此时
n
u
m
s
1
[
i
−
1
]
<
=
n
u
m
s
1
[
i
]
nums1[i-1]<=nums1[i]
nums1[i−1]<=nums1[i]和
n
u
m
s
2
[
j
−
1
]
<
=
n
u
m
s
2
[
j
]
nums2[j-1]<=nums2[j]
nums2[j−1]<=nums2[j]肯定是成立的(因为有序),因此我们需要确定一个
i
i
i,使如下等式成立:
n u m s 1 [ i − 1 ] ≤ n u m s 2 [ j ] 且 n u m s 2 [ j − 1 ] ≤ n u m s 1 [ i ] nums1[i−1]≤nums2[j] 且 nums2[j−1]≤nums1[i] nums1[i−1]≤nums2[j]且nums2[j−1]≤nums1[i]
但该条件等于仅需等价于寻找最大的 i i i使得如下等式成立,因为随着 i i i递增, j j j是减小的,若 i i i是最大的,说明 i + 1 i+1 i+1不成立,此时有 n u m s [ i ] > n u m s 2 [ j − 1 ] nums[i]>nums2[j−1] nums[i]>nums2[j−1].
nums1[i-1]<=nums2[j]$
因此二分条件判断如下:
n u m s 1 [ i − 1 ] < = n u m s 2 [ j ] nums1[i-1] <= nums2[j] nums1[i−1]<=nums2[j]成立,此时说明 i i i可能不是最大值,可进一步探索, l e f t = i + 1 left = i + 1 left=i+1
否则,即 n u m s 1 [ i − 1 ] > n u m s 2 [ j ] nums1[i-1] > nums2[j] nums1[i−1]>nums2[j],说明分界点 i i i大了,故 r i g h t = i − 1 right = i - 1 right=i−1
具体实现见Python方法二和Java实现。
3.代码实现
3.1 Python
方法一:
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
res = []
left, right = 0, 0
m, n = len(nums1), len(nums2)
while left < m and right < n:
if nums1[left] <= nums2[right]:
res.append(nums1[left])
left += 1
else:
res.append(nums2[right])
right += 1
if left < m:
res.extend(nums1[left:])
if right < n:
res.extend(nums2[right:])
mid = (m + n ) // 2
if (m+n) % 2 == 0:
return (res[mid] + res[mid-1]) / 2
else:
return res[mid]
方法二:
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
m, n = len(nums1), len(nums2)
if m > n:
return self.findMedianSortedArrays(nums2, nums1)
infinty = pow(10, 7)
med1, med2 = 0, 0
left, right = 0, m
while left <= right:
i = (left +right) // 2
j = (m + n + 1) // 2 - i
nums1_left = -infinty if i == 0 else nums1[i-1]
nums1_right = infinty if i == m else nums1[i]
nums2_left = -infinty if j == 0 else nums2[j-1]
nums2_right = infinty if j == n else nums2[j]
if nums1_left <= nums2_right:
med1, med2 = max(nums1_left, nums2_left), min(nums1_right, nums2_right)
left = i + 1
else:
right = i - 1
return (med1 + med2) / 2 if (m + n) % 2 == 0 else med1
3.2 Java
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
if (m > n){
return findMedianSortedArrays(nums2, nums1);
}
int left = 0, right = m;
int med1 = 0, med2 = 0;
while (left <= right){
int i = (left + right) / 2;
int j = (m + n + 1) / 2 - i;
int nums1_left = i == 0 ? Integer.MIN_VALUE : nums1[i-1];
int nums1_right = i == m ? Integer.MAX_VALUE : nums1[i];
int nums2_left = j == 0 ? Integer.MIN_VALUE : nums2[j-1];
int nums2_right = j == n ? Integer.MAX_VALUE : nums2[j];
if (nums1_left <= nums2_right){
med1 = Math.max(nums1_left, nums2_left);
med2 = Math.min(nums1_right, nums2_right);
left = i + 1;
}else{
right = i - 1;
}
}
return (m + n) % 2 == 0 ? (med1 + med2) / 2.0 : med1;
}
}
执行用时不必过于纠结,对比可以发现,对于python和java完全相同的编写,java的时间一般是优于python的;至于编写的代码的执行用时击败多少对手,执行用时和网络环境、当前提交代码人数等均有关系,可以尝试完全相同的代码多次执行用时也不是完全相同,只要确保自己代码的算法时间复杂度满足相应要求即可,也可以通过点击分布图查看其它coder的code。