LeetCode Hot 100 --- 寻找两个正序数组的中位数(java详解)
Posted 小样5411
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode Hot 100 --- 寻找两个正序数组的中位数(java详解)相关的知识,希望对你有一定的参考价值。
题目
解法一
时间复杂度:O(m+n)
空间复杂度:O(m+n)
这个也是我自己想出来的解法,不过确实最慢解。。。代码注释也比较详细
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
//首先,对两个数组进行合并
//判空
if(nums1 == null || nums1.length == 0){
//获取数组的中位数
return getMidValue(nums2);
}
if(nums2 == null || nums2.length == 0){
//获取数组的中位数
return getMidValue(nums1);
}
//两个都不为空,则进行合并操作
int m = nums1.length;
int n = nums2.length;
int[] mergeArr = new int[m+n];
int count = 0;//合并数组下标
int p1 = 0;//初始化两个指针(下标)
int p2 = 0;
//下面合并过程很像有序链表的合并
while(p1 < nums1.length && p2 < nums2.length){
if(nums1[p1] < nums2[p2]){
mergeArr[count]=nums1[p1];
count++;
p1++;
}else{
mergeArr[count]=nums2[p2];
count++;
p2++;
}
}
//若nums1数组没有走完,则将剩余加入合并数组
while(p1 < nums1.length){
mergeArr[count]=nums1[p1];
count++;
p1++;
}
//若nums2数组没有走完,则将剩余加入合并数组
while(p2 < nums2.length){
mergeArr[count]=nums2[p2];
count++;
p2++;
}
//完成合并后,list即为合并的数组,转成array数组,找出中位数即可
return getMidValue(mergeArr);
}
//获取数组的中位数
private double getMidValue(int[] arr){
int len = arr.length;
//数组长度为偶数
if(len % 2 == 0){
return (arr[len/2]+arr[len/2-1])/2.0; //这里偶数长度,除以2.0很重要,表示保留一位小数,直接5/2结果是2.00000
}else{
//数组长度为奇数
return arr[len/2];
}
}
}
解法二(优化解法一)
时间复杂度:O(m+n)
空间复杂度:O(1)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int len = m + n;
int left = 0, right = 0;
int aStart = 0, bStart = 0;//下标,aStart对应nums1,bStart对应nums2
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;
}
}
下面是详细解析
用 len 表示合并后数组的长度,如果是奇数,需要遍历 len/2 + 1 次。如果是偶数,我们需要知道第 len/2和 len/2+1 个数,也是需要遍历 len/2+1 次。所以遍历的话,奇数和偶数都是 len/2+1 次
那么循环就可以写成for(int i = 0 ; i <= len/2 ; i++)
,正好 len/2+1 次
如上,返回中位数,奇数需要最后一次遍历的结果就可以,偶数需要最后一次和上一次遍历的结果。所以我们用两个变量 left 和 right,right 保存当前循环的结果,在每次循环前将 right 的值赋给 left。这样在最后一次循环的时候,left 将得到 right 的值,也就是上一次循环的结果,接下来 right 更新为最后一次的结果,也就是left会跟着right。
画图解析执行过程
代码中bStart >= n
是为了控制,第二个数组短,第一个长情况,如下
解法三(二分查找)
时间复杂度:O(log(min(M,N)))
空间复杂度:O(1)
这个可以参考解法链接,很详细,方法三最优解,但理解起来有点难度
public class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
int[] temp = nums1;
nums1 = nums2;
nums2 = temp;
}
int m = nums1.length;
int n = nums2.length;
// 分割线左边的所有元素需要满足的个数 m + (n - m + 1) / 2;
int totalLeft = (m + n + 1) / 2;
// 在 nums1 的区间 [0, m] 里查找恰当的分割线,
// 使得 nums1[i - 1] <= nums2[j] && nums2[j - 1] <= nums1[i]
int left = 0;
int right = m;
while (left < right) {
int i = left + (right - left + 1) / 2;
int j = totalLeft - i;
if (nums1[i - 1] > nums2[j]) {
// 下一轮搜索的区间 [left, i - 1]
right = i - 1;
} else {
// 下一轮搜索的区间 [i, right]
left = i;
}
}
int i = left;
int j = totalLeft - i;
int nums1LeftMax = i == 0 ? Integer.MIN_VALUE : nums1[i - 1];
int nums1RightMin = i == m ? Integer.MAX_VALUE : nums1[i];
int nums2LeftMax = j == 0 ? Integer.MIN_VALUE : nums2[j - 1];
int nums2RightMin = j == n ? Integer.MAX_VALUE : nums2[j];
if (((m + n) % 2) == 1) {
return Math.max(nums1LeftMax, nums2LeftMax);
} else {
return (double) ((Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin))) / 2;
}
}
}
以上是关于LeetCode Hot 100 --- 寻找两个正序数组的中位数(java详解)的主要内容,如果未能解决你的问题,请参考以下文章
leetcode的Hot100系列--3. 无重复字符的最长子串--滑动窗口