Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Contents/00.Introduction/04.Solutions-List.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# LeetCode 题解(已完成 715 道)
# LeetCode 题解(已完成 716 道)

| 题号 | 标题 | 题解 | 标签 | 难度 |
| :------ | :------ | :------ | :------ | :------ |
Expand Down Expand Up @@ -399,6 +399,7 @@
| 0844 | [比较含退格的字符串](https://leetcode.cn/problems/backspace-string-compare/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0844.%20%E6%AF%94%E8%BE%83%E5%90%AB%E9%80%80%E6%A0%BC%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2.md) | 栈、双指针、字符串、模拟 | 简单 |
| 0845 | [数组中的最长山脉](https://leetcode.cn/problems/longest-mountain-in-array/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0845.%20%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E6%9C%80%E9%95%BF%E5%B1%B1%E8%84%89.md) | 数组、双指针、动态规划、枚举 | 中等 |
| 0846 | [一手顺子](https://leetcode.cn/problems/hand-of-straights/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0846.%20%E4%B8%80%E6%89%8B%E9%A1%BA%E5%AD%90.md) | 贪心、数组、哈希、排序 | 中等 |
| 0850 | [矩形面积 II](https://leetcode.cn/problems/rectangle-area-ii/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0850.%20%E7%9F%A9%E5%BD%A2%E9%9D%A2%E7%A7%AF%20II.md) | 线段树、数组、有序集合、扫描线 | 困难 |
| 0851 | [喧闹和富有](https://leetcode.cn/problems/loud-and-rich/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0851.%20%E5%96%A7%E9%97%B9%E5%92%8C%E5%AF%8C%E6%9C%89.md) | 深度优先搜索、图、拓扑排序、数组 | 中等 |
| 0852 | [山脉数组的峰顶索引](https://leetcode.cn/problems/peak-index-in-a-mountain-array/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0852.%20%E5%B1%B1%E8%84%89%E6%95%B0%E7%BB%84%E7%9A%84%E5%B3%B0%E9%A1%B6%E7%B4%A2%E5%BC%95.md) | 数组、二分查找 | 简单 |
| 0860 | [柠檬水找零](https://leetcode.cn/problems/lemonade-change/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0860.%20%E6%9F%A0%E6%AA%AC%E6%B0%B4%E6%89%BE%E9%9B%B6.md) | 贪心、数组 | 简单 |
Expand Down
2 changes: 1 addition & 1 deletion Contents/00.Introduction/05.Categories-List.md
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@
| :------ | :------ | :------ | :------ | :------ |
| 0218 | [天际线问题](https://leetcode.cn/problems/the-skyline-problem/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0218.%20%E5%A4%A9%E9%99%85%E7%BA%BF%E9%97%AE%E9%A2%98.md) | 树状数组、线段树、数组、分治、有序集合、扫描线、堆(优先队列) | 困难 |
| 0391 | [完美矩形](https://leetcode.cn/problems/perfect-rectangle/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0391.%20%E5%AE%8C%E7%BE%8E%E7%9F%A9%E5%BD%A2.md) | 数组、扫描线 | 困难 |
| 0850 | 矩形面积 II | | | |
| 0850 | [矩形面积 II](https://leetcode.cn/problems/rectangle-area-ii/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0850.%20%E7%9F%A9%E5%BD%A2%E9%9D%A2%E7%A7%AF%20II.md) | 线段树、数组、有序集合、扫描线 | 困难 |

### 树状数组题目

Expand Down
2 changes: 1 addition & 1 deletion Contents/07.Tree/03.Segment-Tree/02.Segment-Tree-List.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@
| :------ | :------ | :------ | :------ | :------ |
| 0218 | [天际线问题](https://leetcode.cn/problems/the-skyline-problem/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0218.%20%E5%A4%A9%E9%99%85%E7%BA%BF%E9%97%AE%E9%A2%98.md) | 树状数组、线段树、数组、分治、有序集合、扫描线、堆(优先队列) | 困难 |
| 0391 | [完美矩形](https://leetcode.cn/problems/perfect-rectangle/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0391.%20%E5%AE%8C%E7%BE%8E%E7%9F%A9%E5%BD%A2.md) | 数组、扫描线 | 困难 |
| 0850 | 矩形面积 II | | | |
| 0850 | [矩形面积 II](https://leetcode.cn/problems/rectangle-area-ii/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/0850.%20%E7%9F%A9%E5%BD%A2%E9%9D%A2%E7%A7%AF%20II.md) | 线段树、数组、有序集合、扫描线 | 困难 |

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,4 @@
- [动态规划优化题目](./Contents/10.Dynamic-Programming/11.DP-Optimization/04.DP-Optimization-List.md)

## 11. 附加内容
## [12. LeetCode 题解(已完成 715 道)](./Contents/00.Introduction/04.Solutions-List.md)
## [12. LeetCode 题解(已完成 716 道)](./Contents/00.Introduction/04.Solutions-List.md)
79 changes: 56 additions & 23 deletions Solutions/0004. 寻找两个正序数组的中位数.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,77 @@

## 题目大意

给定两个正序(从小到大排序)数组 nums1、nums2。要求找出并返回这两个正序数组的中位数。
**描述**:给定两个正序(从小到大排序)数组 `nums1`、`nums2`。

**要求**:找出并返回这两个正序数组的中位数。

**说明**:

- 算法的时间复杂度应该为 $O(log (m+n))$ 。
- $nums1.length == m$。
- $nums2.length == n$。
- $0 \le m \le 1000$。
- $0 \le n \le 1000$。
- $1 \le m + n \le 2000$。
- $-10^6 \le nums1[i], nums2[i] \le 10^6$。

**示例**:

```Python
输入 nums1 = [1,2], nums2 = [3,4]
输出 2.50000
解释 合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
```

## 解题思路

单个有序数组的中位数是中间元素位置的元素。如果中间元素位置有两个元素,则为两个元素的平均数。如果是两个有序数组,则可以使用归并排序的方式将两个数组拼接为一个大的有序数组。大的有序数组中间位置的元素,即为中位数。
### 思路 1:二分查找

单个有序数组的中位数是中间元素位置的元素。如果中间元素位置有两个元素,则为两个元素的平均数。如果是两个有序数组,则可以使用归并排序的方式将两个数组拼接为一个大的有序数组。合并后有序数组中间位置的元素,即为中位数。

当然不合并的话,我们只需找到中位数的位置即可。我们用 n1、n2 来表示数组 nums1nums2 的长度,则合并后的大的有序数组长度为 (n1 + n2)。
当然不合并的话,我们只需找到中位数的位置即可。我们用 $n1$、$n2$ 来表示数组 $nums1$、$nums2$ 的长度,则合并后的大的有序数组长度为 $(n1 + n2)$

同时可以发现:**中位数把数组分割成了左右两部分,并且左右两部分元素个数相等。**
我们可以发现:**中位数把数组分割成了左右两部分,并且左右两部分元素个数相等。**

- 如果 $(n1 + n2)$ 是奇数时,中位数是大的有序数组中第 $\lfloor \frac{(n1 + n2)}{2} \rfloor + 1$ 的元素,单侧元素个数为 $\lfloor \frac{(n1 + n2)}{2} \rfloor + 1$ 个(包含中位数)。
- 如果 $(n1 + n2)$ 是偶数时,中位数是第 $\lfloor \frac{(n1 + n2)}{2} \rfloor$ 的元素和第 $\lfloor \frac{(n1 + n2)}{2} \rfloor + 1$ 的元素的平均值,单侧元素个数为 $\lfloor \frac{(n1 + n2)}{2} \rfloor$ 个。

因为是向下取整,上面两种情况综合可以写为:单侧元素个数为:$\lfloor \frac{(n1 + n2 + 1)}{2} \rfloor$ 个。

我们用 $k$ 来表示 $\lfloor \frac{(n1 + n2 + 1)}{2} \rfloor$ 。现在的问题就变为了:**如何在两个有序数组中找到前 k 个元素?**
我们用 $k$ 来表示 $\lfloor \frac{(n1 + n2 + 1)}{2} \rfloor$ 。现在的问题就变为了:**如何在两个有序数组中找到前 k 小的元素位置?**

如果我们从 nums1 数组中取出 m1 个元素,那么从 nums2 就需要取出 m2 = k - m1 个元素。
如果我们从 $nums1$ 数组中取出前 $m1(m1 \le k)$ 个元素,那么从 $nums2$ 就需要取出前 $m2 = k - m1$ 个元素。

问题就可以进一步转换为:**从 nums1 数组中取出 m1 个元素,那么从 nums2 就需要取出 k - m1 个元素,使得 nums1 第 m1 个元素或 nums2 第 k - m1 个元素为中位线位置**
并且如果我们在 $nums1$ 数组中找到了合适的 $m1$ 位置,则 $m2$ 的位置也就确定了

可以通过「二分查找」来找到合适的 m1 位置
问题就可以进一步转换为:**如何从 $nums1$ 数组中取出前 $m1$ 个元素,使得 $nums1$ 第 $m1$ 个元素或者 $nums2$ 第 $m2 = k - m1$ 个元素为中位线位置**

让 left 指向 $nums1$ 的头部位置 0,right 指向 $nums1$ 的尾部位置 n1。
我们可以通过「二分查找」的方法,在数组 $nums1$ 中找到合适的 $m1$ 位置,具体做法如下:

每次取中间位置 $mid$,判断 $nums1$ 第 $mid+1$ 个元素和 $nums2$ 第 $k - (mid+1)$个元素之间的关系,即 $nums1[mid]$ 和 $nums2[k- mid - 1]$ 的关系。
1. 让 $left$ 指向 $nums1$ 的头部位置 $0$,$right$ 指向 $nums1$ 的尾部位置 $n1$。
2. 每次取中间位置作为 $m1$,则 $m2 = k - m1$。然后判断 $nums1$ 第 $m1$ 位置上元素和 $nums2$ 第 $m2 - 1$ 位置上元素之间的关系,即 $nums1[m1]$ 和 $nums2[m2 - 1]$ 的关系。
1. 如果 $nums1[m1] < nums2[m2 - 1]$,则 $nums1$ 的前 $m1$ 个元素都不可能是第 $k$ 个元素。说明 $m1$ 取值有点小了,应该将 $m1$ 进行右移操作,即 $left = m1 + 1$。
2. 如果 $nums1[m1] \ge nums2[m2 - 1]$,则说明 $m1$ 取值可能有点大了,应该将 $m1$ 进行左移。根据二分查找排除法的思路(排除一定不存在的区间,在剩下区间中继续查找),这里应取 $right = m1$。
3. 找到 $m1$ 的位置之后,还要根据两个数组长度和 $(n1 + n2)$ 的奇偶性,以及边界条件来计算对应的中位数。

如果 $nums1[mid] < nums2[k- mid - 1]$,则 $nums1$ 中比 $nums1[mid]$ 小的元素有 $mid$ 个,$nums2$ 数组中即便是前 $k- mid - 2$ 个元素都比 $nums1[mid]$ 小,也最多有 $k- mid - 2$ 个元素比 $nums1[mid]$ 小。所以比 $nums1[mid]$ 小的元素最多有 $mid + (k - mid - 2) = k - 2$ 个。所以 $nums1$ 的前 $mid$ 个元素都不可能是第 $k$ 个元素。可以全部排除。
---

简单可以描述为
上面之所以要判断 $nums1[m1]$ 和 $nums2[m2 - 1]$ 的关系是因为

- 如果 $nums1[mid] < nums2[k- mid - 1]$,则 $nums1$ 的前 $mid$ 个元素都不可能是第 $k$ 个元素,可以全部排除。
> 如果 $nums1[m1] < nums2[m2 - 1]$,则说明:
>
> - 最多有 $m1 + m2 - 1 = k - 1$ 个元素比 $nums1[m1]$ 小,所以 $nums1[m1]$ 左侧的 $m1$ 个元素都不可能是第 $k$ 个元素。可以将 $m1$ 左侧的元素全部排除,然后将 $m1$ 进行右移。

- 如果 $nums1[mid] > nums2[k- mid - 1]$,则 $nums2$ 的前 $mid$ 个元素都不可能是第 $k$ 个元素,可以全部排除。
- 如果 $nums1[mid] == nums2[k- mid - 1]$,可以按第一种情况处理。
推理过程:

找到 m1 的位置之后,还要根据两个数组长度和 $(n1 + n2)$ 的奇偶性,以及边界条件来计算对应的中位数。
如果 $nums1[m1] < nums2[m2 - 1]$,则:

## 代码
1. $nums1[m1]$ 左侧比 $nums1[m1]$ 小的一共有 $m1$ 个元素($nums1[0] ... nums1[m1 - 1]$ 共 $m1$ 个)。
2. $nums2$ 数组最多有 $m2 - 1$ 个元素比 $nums1[m1]$ 小(即便是 $nums2[m2 - 1]$ 左侧所有元素都比 $nums1[m1]$ 小,也只有 $m2 - 1$ 个)。
3. 综上所述,$nums1$、$nums2$ 数组中最多有 $m1 + m2 - 1 = k - 1$ 个元素比 $nums1[m1]$ 小。
4. 所以 $nums1[m1]$ 左侧的 $m1$ 个元素($nums1[0] ... nums1[m1 - 1]$)都不可能是第 $k$ 个元素。可以将 $m1$ 左侧的元素全部排除,然后将 $m1$ 进行右移。

### 思路 1:二分查找代码

```Python
class Solution:
Expand All @@ -57,16 +89,17 @@ class Solution:
left = 0
right = n1
while left < right:
mid = left + (right - left) // 2
if nums1[mid] < nums2[k-1-mid]:
left = mid + 1
m1 = left + (right - left) // 2 # 在 nums1 中取前 m1 个元素
m2 = k - m1 # 在 nums2 中取前 m2 个元素
if nums1[m1] < nums2[m2 - 1]: # 说明 nums1 中所元素不够多,
left = m1 + 1
else:
right = mid
right = m1

m1 = left
m2 = k - m1

c1 = max(float('-inf') if m1 <= 0 else nums1[m1-1], float('-inf') if m2 <= 0 else nums2[m2 - 1])
c1 = max(float('-inf') if m1 <= 0 else nums1[m1 - 1], float('-inf') if m2 <= 0 else nums2[m2 - 1])
if (n1 + n2) % 2 == 1:
return c1

Expand Down
Loading