Merge pull request #86 from freetreer/master

0004_Solved
This commit is contained in:
程序员吴师兄 2020-05-09 09:01:09 +08:00 committed by GitHub
commit 76b7c5f2ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 205 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,151 @@
# LeetCode 4 号问题寻找两个正序数组的中位数
> 本文首发于公众号图解面试算法 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一
>
> 同步博客https://www.algomooc.com
题目来源于 LeetCode 上第 4 号问题寻找两个正序数组的中位数题目难度为 Hard目前通过率为 29.0%
#### 题目描述
> 给定两个大小为 m n 的正序从小到大数组 nums1  nums2
请你找出这两个正序数组的中位数并且要求算法的时间复杂度为 O(log(m + n))
你可以假设 nums1  nums2 不会同时为空
```java
示例1
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例2
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
```
#### 题目解析
这道题网络上的解析都非常高深很难理解私以为它们都将简单的问题复杂化了本题在一些处理上确实会有些麻烦比如数组边界的处理和偶数个数的中位数的处理但其核心思想并不复杂
首先我们可以只考虑数字总个数为奇数的情况让我们看下下图
![](../Animation/image1.PNG)
蓝框是中位数左边的数包括中位数而橘框则为中位数右边的数
3个显然的规则
1.两个数组的蓝框总个数=(数字总个数+1)/2;
2.所有蓝框内的数都小于橘框内的数
3.中位数为蓝框中最大的那一位即数组1蓝框最后一位或数组2蓝框最后一位
![](../Animation/image2.PNG)
如图我们要找到一组AB满足上面3条规则
对于规则1我们在数组1中找任意A然后根据规则1就能推算出对应的B的位置
对于规则2由于数组1和2都是有序数组即X1<A<Y1;X2<B<Y2我们实际上只需要判断A是否小于Y2以及B是否小于Y2
对于规则3由于数组1和2都是有序数组因此中位数为A,B中较大的那一项
那么具体该如何操作呢?
由于数组1和2都是有序数组且题目要求O(log(m+n))复杂度我们明显应考虑二分法
**情况1**
![](../Animation/case1.png)
首先我们选择数组1进行操作取其中间值9 因此 A=9) 根据规则1,我们在数组2中找到对应值B = 4一共有11个数(11+1) / 2 = 6因此蓝色框总数为6
紧接着我们根据规则2判断A(9)是否小于B.next(5)以及B(4)是否小于A.next(11)
显然A比B.next,也就是一个橘框还要大这是不允许的可见A只能取比9更小的数字了如果取更大的数字那B就会更小更不可能满足规则2所以这种情况下我们要向左进行二分
**情况2**
![](../Animation/case2.png)
这种情况下B比A.next,也就是一个橘框还要大这是不允许的可见A只能取比9更大的数字了如果取更小的数字那B就会更大更不可能满足规则2所以这种情况下我们要向右进行二分
**情况3**
![](../Animation/case3.png)
随着我们不断地二分中位数显然必然会出现
如图上这种情况A小于B.next且B小于A.next
那么显然A,B中较大的那一项就是中位数规则3
本题算法的核心思想就是这样简单此外当数字总数为偶数时我们需要把我们求得的中位数"与它下一项相加并除以2即可。由于本题中数字可能相同所以大小的比较需要使用>=和<=。
下面提供了作者的一份代码leetcode上的结果为执行用时2 ms内存消耗40.3 MB都超过了100%的用户读者可以参考一下
#### 代码实现
Java语言
```java
public class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
// 使nums1成为较短数组,不仅可以提高检索速度,同时可以避免一些边界问题
if (nums1.length > nums2.length) {
int[] temp = nums1;
nums1 = nums2;
nums2 = temp;
}
int len1 = nums1.length;
int len2 = nums2.length;
int leftLen = (len1 + len2 + 1) / 2; //两数组合并&排序后,左半边的长度
// 对数组1进行二分检索
int start = 0;
int end = len1;
while (start <= end) {
// 两个数组的被测数A,B的位置(从1开始计算)
// count1 = 2 表示 num1 数组的第2个数字
// 比index大1
int count1 = start + ((end - start) / 2);
int count2 = leftLen - count1;
if (count1 > 0 && nums1[count1 - 1] > nums2[count2]) {
// A比B的next还要大
end = count1 - 1;
} else if (count1 < len1 && nums2[count2 - 1] > nums1[count1]) {
// B比A的next还要大
start = count1 + 1;
} else {
// 获取中位数
int result = (count1 == 0)? nums2[count2 - 1]: // 当num1数组的数都在总数组右边
(count2 == 0)? nums1[count1 - 1]: // 当num2数组的数都在总数组右边
Math.max(nums1[count1 - 1], nums2[count2 - 1]); // 比较A,B
if (isOdd(len1 + len2)) {
return result;
}
// 处理偶数个数的情况
int nextValue = (count1 == len1) ? nums2[count2]:
(count2 == len2) ? nums1[count1]:
Math.min(nums1[count1], nums2[count2]);
return (result + nextValue) / 2.0;
}
}
return Integer.MIN_VALUE; // 绝对到不了这里
}
// 奇数返回true,偶数返回false
private boolean isOdd(int x) {
return (x & 1) == 1;
}
}
```
#### 动画理解
![](../Animation/Animation.gif)
#### 复杂度分析
+ 时间复杂度对数组进行二分查找因此为O(logN)
+ 空间复杂度O(1)
![](../../Pictures/qrcode.jpg)

View File

@ -0,0 +1,54 @@
public class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
// 使nums1成为较短数组,不仅可以提高检索速度,同时可以避免一些边界问题
if (nums1.length > nums2.length) {
int[] temp = nums1;
nums1 = nums2;
nums2 = temp;
}
int len1 = nums1.length;
int len2 = nums2.length;
int leftLen = (len1 + len2 + 1) / 2; //两数组合并&排序后,左半边的长度
// 对数组1进行二分检索
int start = 0;
int end = len1;
while (start <= end) {
// 两个数组的被测数A,B的位置(从1开始计算)
// count1 = 2 表示 num1 数组的第2个数字
// 比index大1
int count1 = start + ((end - start) / 2);
int count2 = leftLen - count1;
if (count1 > 0 && nums1[count1 - 1] > nums2[count2]) {
// A比B的next还要大
end = count1 - 1;
} else if (count1 < len1 && nums2[count2 - 1] > nums1[count1]) {
// B比A的next还要大
start = count1 + 1;
} else {
// 获取中位数
int result = (count1 == 0)? nums2[count2 - 1]: // 当num1数组的数都在总数组右边
(count2 == 0)? nums1[count1 - 1]: // 当num2数组的数都在总数组右边
Math.max(nums1[count1 - 1], nums2[count2 - 1]); // 比较A,B
if (isOdd(len1 + len2)) {
return result;
}
// 处理偶数个数的情况
int nextValue = (count1 == len1) ? nums2[count2]:
(count2 == len2) ? nums1[count1]:
Math.min(nums1[count1], nums2[count2]);
return (result + nextValue) / 2.0;
}
}
return Integer.MIN_VALUE; // 绝对到不了这里
}
// 奇数返回true,偶数返回false
private boolean isOdd(int x) {
return (x & 1) == 1;
}
}