题目:
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
You may assume nums1 and nums2 cannot be both empty.
Example 1:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0
Example 2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
方法1:
类似于归并排序,将两个有序列表合并为一个大的有序列表,然后根据列表的元素是奇数还是偶数分别求中间值,但这种方法复杂度O(m+n) 不符合题目要求
C++:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
int ans[n + m];
double median;
int i = 0, j = 0, index = 0;
while(i < m && j < n){
if(nums1[i] <= nums2[j]){
ans[index++] = nums1[i++];
} else {
ans[index++] = nums2[j++];
}
}
while(i < m) ans[index++] = nums1[i++];
while(j < n) ans[index++] = nums2[j++];
if(index % 2){
median = (double)ans[index / 2];
} else {
median = (ans[index / 2] + ans[index / 2 - 1]) / 2.0;
}
return median;
}
};
java:
java中整数不能直接当布尔值来用 所以不能用if(index % 2) 而是用if(index % 2 == 1)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int[] ans = new int[m + n];
int i = 0, j = 0, index = 0;
double median;
while(i < m && j < n){
if(nums1[i] <= nums2[j]){
ans[index++] = nums1[i++];
} else {
ans[index++] = nums2[j++];
}
}
while(i < m) ans[index++] = nums1[i++];
while(j < n) ans[index++] = nums2[j++];
if(index % 2 == 1){
median = (double)ans[index / 2];
} else {
median = (ans[index / 2] + ans[index / 2 - 1]) / 2.0;
}
return median;
}
}
Python:
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
m = len(nums1)
n = len(nums2)
ans = [0] * (n + m)
i, j , index = 0, 0, 0
while i < m and j < n:
if nums1[i] <= nums2[j]:
ans[index] = nums1[i]
i = i + 1
index = index + 1
else:
ans[index] = nums2[j]
j = j + 1
index = index + 1
while i < m:
ans[index] = nums1[i]
i = i + 1
index = index + 1
while j < n:
ans[index] = nums2[j]
j = j + 1
index = index + 1
# print ans
if index % 2:
median = ans[index / 2]
else:
median = (ans[index / 2] + ans[index / 2 - 1]) / 2.0
return median;
方法2:
时间复杂度为O(log (m+n)),所以要用到二分法
核心方法是findKth函数,找到第两个数组第k小的数
首先我们要判断两个数组长度之和是否为奇数(用&0x1速度比较快)分两种情况
好,这里我们需要定义一个函数来在两个有序数组中找到第K个元素,下面重点来看如何实现找到第K个元素。首先,为了避免产生新的数组从而增加时间复杂度,我们使用两个变量i和j分别来标记数组nums1和nums2的起始位置。然后来处理一些corner cases,比如当某一个数组的起始位置大于等于其数组长度时,说明其所有数字均已经被淘汰了,相当于一个空数组了,那么实际上就变成了在另一个数组中找数字,直接就可以找出来了。还有就是如果K=1的话,那么我们只要比较nums1和nums2的起始位置i和j上的数字就可以了。难点就在于一般的情况怎么处理?因为我们需要在两个有序数组中找到第K个元素,为了加快搜索的速度,我们要使用二分法,那么对谁二分呢,数组么?其实要对K二分,意思是我们需要分别在nums1和nums2中查找第K/2个元素,注意这里由于两个数组的长度不定,所以有可能某个数组没有第K/2个数字,所以我们需要先check一下,数组中到底存不存在第K/2个数字,如果存在就取出来,否则就赋值上一个整型最大值。如果某个数组没有第K/2个数字,那么我们就淘汰另一个数组的前K/2个数字即可。举个例子来说吧,比如 nums1 = {3},nums2 = {2, 4, 5, 6, 7},K=4,我们要找两个数组混合中第4个数字,那么我们分别在 nums1 和 nums2 中找第2个数字,我们发现 nums1 中只有一个数字,不存在第二个数字,那么 nums2 中的前2个数字可以直接跳过,为啥呢,因为我们要求整个混合数组的第4个数字,不管 nums1 中的那个数字是大是小,第4个数字绝不会出现在 nums2 的前两个数字中,所以可以直接跳过。
有没有可能两个数组都不存在第K/2个数字呢,这道题里是不可能的,因为我们的K不是任意给的,而是给的m+n的中间值,所以必定至少会有一个数组是存在第K/2个数字的。最后就是二分法的核心啦,比较这两个数组的第K/2小的数字midVal1和midVal2的大小,如果第一个数组的第K/2个数字小的话,那么说明我们要找的数字肯定不在nums1中的前K/2个数字,所以我们可以将其淘汰,将nums1的起始位置向后移动K/2个,并且此时的K也自减去K/2,调用递归。反之,我们淘汰nums2中的前K/2个数字,并将nums2的起始位置向后移动K/2个,并且此时的K也自减去K/2,调用递归即可(by [LeetCode] Median of Two Sorted Arrays 两个有序数组的中位数)
注意点: 不能判断midVal1和midVal2相等时,返回midVal1,在某些样例会出错
if(midA < midB){
return findKth(A, i + k / 2, B, j, k - k / 2);
} else if(midA > midB) {
return findKth(A, i, B, j + k / 2, k - k / 2);
} else {
return midA;
}
如:
A:[1,2]
B:[1,2]
查找两个数组的第2小的数时,k=2,k/2 = 1,第一次二分都指向元素的首元素,它们都相等,就返回1,此时正确
但查找两个数组的第3小的数时,k=3,k/2 = 1,第一次二分都指向元素的首元素,它们都相等,就返回1,此时不正确,应该返回2
改进方法:
三分支改为两分支
if(midA < midB){
return findKth(A, i + k / 2, B, j, k - k / 2);
} else {
return findKth(A, i, B, j + k / 2, k - k / 2);
}
A:[1,2]
B:[1,2]
查找两个数组的第2小的数时,k=2,k/2 = 1,第一次二分都指向元素的首元素,它们相等,不满足midA < midB,将B数组前一半舍弃,j = 1,k = 1,到达递归边界,取A[0]和B[1]中小者,即为1,正确
查找两个数组的第3小的数时,k=3,k/2 = 1,第一次二分都指向元素的首元素,它们相等,不满足midA < midB,将B数组前一半舍弃,j = 1,k = 2,k/2 = 1,第二次二分,A[0] < B[1],将A数组前一半舍弃,i= 1,k=1,取A[1]和B[1]中小者,即为2,正确
C++:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
int total = m + n;
//如果总长度是奇数
if(total & 0x1){
return (double)findKth(nums1, 0, nums2, 0, total / 2 + 1);
} else {
// cout << findKth(nums1, 0, nums2, 0, total / 2) << endl;
//cout << findKth(nums1, 0, nums2, 0, total / 2 + 1) << endl;
return (findKth(nums1, 0, nums2, 0, total / 2) + findKth(nums1, 0, nums2, 0, total / 2 + 1)) / 2.0;
}
}
//寻找A和B数组中第k小的数
//i,j分别为指针 指向开始查找的位置
int findKth(vector<int>& A, int i, vector<int>& B, int j, int k){
//i超过A数组的边界 说明A中没有要查找的元素 直接返回B中的元素
if(i >= A.size()) return B[j + k - 1];
//j超过B数组的边界 说明B中没有要查找的元素 直接返回A中的元素
if(j >= B.size()) return A[i + k - 1];
if(k == 1) return min(A[i], B[j]);
//两个数组同时二分
//每个数组找到第k/2个元素 如果越界 取最大值INT_MAX
int midA = ((i + k / 2 - 1) < A.size()) ? A[i + k / 2 - 1] : INT_MAX;
int midB = ((j + k / 2 - 1) < B.size()) ? B[j + k / 2 - 1] : INT_MAX;
if(midA < midB){
return findKth(A, i + k / 2, B, j, k - k / 2);
} else {
return findKth(A, i, B, j + k / 2, k - k / 2);
}
}
};
java:
注意点:Java中整数最大值为Integer.MAX_VALUE
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int total = m + n;
//如果总长度是奇数
if(total % 2 == 1){
return (double)findKth(nums1, 0, nums2, 0, total / 2 + 1);
} else {
return (findKth(nums1, 0, nums2, 0, total / 2) + findKth(nums1, 0, nums2, 0, total / 2 + 1)) / 2.0;
}
}
//寻找A和B数组中第k小的数
//i,j分别为指针 指向开始查找的位置
int findKth(int[] A, int i, int[] B, int j, int k){
//i超过A数组的边界 说明A中没有要查找的元素 直接返回B中的元素
if(i >= A.length) return B[j + k - 1];
//j超过B数组的边界 说明B中没有要查找的元素 直接返回A中的元素
if(j >= B.length) return A[i + k - 1];
if(k == 1) return Math.min(A[i], B[j]);
//两个数组同时二分
//每个数组找到第k/2个元素 如果越界 取最大值INT_MAX
int midA = ((i + k / 2 - 1) < A.length) ? A[i + k / 2 - 1] : Integer.MAX_VALUE;
int midB = ((j + k / 2 - 1) < B.length) ? B[j + k / 2 - 1] : Integer.MAX_VALUE;
if(midA < midB){
return findKth(A, i + k / 2, B, j, k - k / 2);
} else {
return findKth(A, i, B, j + k / 2, k - k / 2);
}
}
}
Python:
注意点:def findKth(A, i, B, j, k)定义在方法里
整数最大值:sys.maxsize
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
def findKth(A, i, B, j, k):
if i >= len(A): return B[j + k - 1]
if j >= len(B): return A[i + k - 1]
if k == 1: return min(A[i], B[j])
midA = A[i + k / 2 - 1] if (i + k / 2 - 1) < len(A) else sys.maxsize
midB = B[j + k / 2 - 1] if (j + k / 2 - 1) < len(B) else sys.maxsize
if midA < midB:
return findKth(A, i + k / 2, B, j, k - k / 2)
else:
return findKth(A, i, B, j + k / 2, k - k / 2)
m = len(nums1)
n = len(nums2)
total = m + n
if total % 2 == 1:
return findKth(nums1, 0, nums2, 0, total // 2 + 1)
else:
return (findKth(nums1, 0, nums2, 0, total // 2) + findKth(nums1, 0, nums2, 0, total // 2 + 1)) / 2.0
方法3:
迭代法:
核心是理解中位数的概念是左右部分相等,且左部分的值小于右部分的值
用值cut1和cut2将两数组分为左右部分,使得左右部分相等或者右部分的数比左部分多一位
定义l1,l2为左部分的最大值,为nums1[cut1 - 1],nums2[cut1 - 1]
r1,r2为右部分的最小值,为nums1[cut1],nums2[cut1]
为实现两部分相等需满足cut2 = (m + n) / 2 - cut1
为了确保cut2不为负数,即要求二分的数组是长度较小的数组,现在较小的数组中二分确定cut1,然后确定cut2
cpp:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
if(m > n) return findMedianSortedArrays(nums2, nums1);
int lo = 0;
int hi = m;
while(lo <= hi){
int cut1 = (lo + hi) / 2;
int cut2 = (m + n) / 2 - cut1;
int l1 = (cut1 == 0) ? INT_MIN : (nums1[cut1 - 1]);
int l2 = (cut2 == 0) ? INT_MIN : (nums2[cut2 - 1]);
int r1 = (cut1 == m) ? INT_MAX : (nums1[cut1]);
int r2 = (cut2 == n) ? INT_MAX : (nums2[cut2]);
if(l1 > r2){
hi = cut1 - 1;
} else if(l2 > r1) {
lo = cut1 + 1;
} else {
return (m + n) % 2 ? min(r1, r2) : (max(l1, l2) + min(r1, r2)) / 2.0;
}
}
return 0.0;
}
};
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 lo = 0;
int hi = m;
while(lo <= hi){
int cut1 = (lo + hi) / 2;
int cut2 = (m + n) / 2 - cut1;
int l1 = (cut1 == 0) ? Integer.MIN_VALUE : (nums1[cut1 - 1]);
int l2 = (cut2 == 0) ? Integer.MIN_VALUE : (nums2[cut2 - 1]);
int r1 = (cut1 == m) ? Integer.MAX_VALUE : (nums1[cut1]);
int r2 = (cut2 == n) ? Integer.MAX_VALUE : (nums2[cut2]);
if(l1 > r2){
hi = cut1 - 1;
} else if(l2 > r1) {
lo = cut1 + 1;
} else {
return (m + n) % 2 == 1? Math.min(r1, r2) : (Math.max(l1, l2) + Math.min(r1, r2)) / 2.0;
}
}
return 0.0;
}
}
python:
不能用 findMedianSortedArrays(nums2, nums1)
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
m , n = len(nums1), len(nums2)
if m > n:
nums1, m, nums2, n = nums2, n, nums1, m
lo = 0
hi = m
while lo <= hi:
cut1 = (lo + hi) >> 1
# print cut1
cut2 = ((m + n) >> 1) - cut1
# print cut2
l1 = -sys.maxsize - 1 if (cut1 == 0) else (nums1[cut1 - 1])
l2 = -sys.maxsize - 1 if (cut2 == 0) else (nums2[cut2 - 1])
r1 = sys.maxsize if (cut1 == m) else (nums1[cut1])
r2 = sys.maxsize if (cut2 == n) else (nums2[cut2])
if l1 > r2:
hi = cut1 - 1
elif l2 > r1:
lo = cut1 + 1
else:
if (m + n) % 2:
return min(r1, r2)
else:
return (max(l1, l2) + min(r1, r2)) / 2.0
return 0.0;