寻找两个正序数组的中位数

方法一:将两个数组合并,然后根据奇偶返回中位数。
- 分别判断数组1与数组2是否为空。
- 建立新数组,将数组1与数组2根据大小放入。
- 根据新数组的奇偶,返回中位数。
完整代码如下
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
if(m == 0){
if(n % 2 == 0){
return (nums2[n/2-1] + nums2[n/2])/2;
}else {
return nums2[n/2];
}
}
if(n == 0){
if(m % 2 == 0){
return (nums1[n/2-1] + nums1[n/2])/2;
}else{
return nums1[n/2];
}
}
int count = 0,i = 0,j = 0;
int nums[] = new int[m + n];
while(count != i + j){
if(i == m){
while(j != n){
nums[count++] = nums2[j++];
}
}
if(j == n){
while(i != m){
nums[count++] = nums1[i++];
}
}
if(nums1[i] < nums2[j]){
nums[count++] = nums1[i++];
}else{
nums[count++] = nums1[j++];
}
}
if(count % 2 == 0){
return (nums[count / 2 - 1] + nums[count / 2]) / 2.0;
} else {
return nums[count / 2];
}
}
- 时间复杂度O(m+n),空间复杂度O(m+n)。
方法二:不需要真正合并,只需要遍历找到中位数即可。
- 用len表示合并后数组长度,若len为奇数,则只需要遍历到len/2个数,若len为偶数,则需要遍历出len/2,与len/2-1这两个数,所以遍历的话,奇数和偶数都是 len/2+1 次。
- 若为奇数,则只需要保存最后一个,也就是len/2个数就可以,若为偶数,需要保存两个数,最后一个和倒数第二个。所以我们使用两个变量left保存上一次遍历的数值,right保存当前遍历数值。
- 遍历时按大小遍历,若A数组的值小于B数组且A数组未遍历完,则A数组向后移动一位,反之B数组向后移动一位。
- 每次都需要判断A,B两数组是否遍历完,所以当A数组未遍历完 && (B数组已至最后一位 || A数组的值小于B数组)时,取A数组的值,反之则取B数组的值。
代码如下
public double findMedianSortedArrays1(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int left = -1, right = -1;
int len = m + n;
int aStart = 0, bStart = 0;
for (int i = 0; i <= len / 2; i++) {
left = right;
if (aStart < m && (bStart >= n || nums1[aStart] < nums2[bStart])) {
right = nums1[aStart++];
} else {
right = nums2[bStart++];
}
}
if (len % 2 == 0) {
return (left + right) / 2.0;
} else {
return right;
}
}
- 时间复杂度为O(m+n),空间复杂度为O(1)。
注意,必须考虑中位数为小数的情况,所以除数是2.0
方法三:使用二分法
-
在方法二中我们每次遍历其实是相当于排除一个不可能是中位数的,那么能不能一次排除多个呢,这里就可以使用二分法。
-
二分法查找的思路如下:
(1)首先,从数组的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行下一步。
(2)如果目标元素大于/小于中间元素,则在数组大于/小于中间元素的那一半区域查找,然后重复步骤(1)的操作。
(3)如果某一步数组为空,则表示找不到目标元素。 -
在本题中,假设数组为
int[] nums1 = new int[]{1, 2, 3, 4, 5, 8, 9,12}; int[] nums2 = new int[]{6, 7, 10, 11}; -
我们需要查找的数组总长度为11,那我们就需要找到(11+1)/2第6个数,他就是中位数,那么k=6/2=3,首先比较两个数组的第三个数,由于3 < 10,那么num1数组的1,2,3需要排除掉,此时
nums1 = {4, 5, 8, 9,12},nums1 = {6, 7, 10, 11} -
本来需要找到第6个数,现在排除了3个,那么我们现在只需要找到第6 - 3 = 3 ,也就是找到剩余两个数组中第三小的数字,这就是我们要找的中位数。
-
比较剩余数组的第三个数,8 < 10,那么num1数组的4,5,8继续排除掉,此时
nums1 = {9,12},nums1 = {6, 7, 10, 11},本来要找第三小的数字,排除三个数后,剩下数组中排第1小的数字就是我们要找的中位数,即中位数 = 6。 -
注意遍历数组时不能超出数组长度,如果发现k/2大于数组长度,那么这个数组就是为空了,所以我们另设一个程序出口,当有一方数组为空的时候,此时剩下数组的第k个数就是我们要找的中位数。
代码如下
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int left = (m + n + 1) / 2;
int right = (m + n + 2) / 2;
//合并奇数和偶数情况,对于3,无论+1还是+2都是要找第2个数,4/2=2,5/2=2
return (getKth(nums1, 0, m - 1, nums2, 0, n - 1, left) + getKth(nums1, 0, m - 1, nums2, 0, n - 1, right)) / 2.0;
}
public int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k){
int len1 = end1 - start1 + 1;
int len2 = end2 - start2 + 1;
//返回情况3种
//返回情况1,数组1为空
if(len1 == 0){
return nums2[start2 + k - 1];
}
//返回情况2,数组2为空
if(len2 == 0){
return nums1[start1 + k - 1];
}
//返回情况3,k=1,返回第k个数
if(k == 1){
return Math.min(nums1[start1],nums2[start2]);
}
//指针向前推进k/2,为什么-1?因为数组是从0开始的
int i = start1 + Math.min(len1, k / 2) - 1;
int j = start2 + Math.min(len2, k / 2) - 1;
//K向前推进
//数组1向前推进
if(nums1[i] < nums2[j]){
return getKth(nums1,i + 1,end1, nums2, start2, end2, k - (i - start1 + 1));
}else{
return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
}
}
- 时间复杂度O(log(m+n)),空间复杂度O(1)。

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
请你找出这两个正序数组的中位数
浙公网安备 33010602011771号