diff --git a/README.md b/README.md index b065f78..5ca4ee8 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ My LeetCode solutions with Chinese explanation. 我的LeetCode中文题解。 | 1 |[Two Sum](https://leetcode.com/problems/two-sum)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/1.%20Two%20Sum.md)|Easy| | | 2 |[Add Two Numbers](https://leetcode.com/problems/add-two-numbers)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/2.%20Add%20Two%20Numbers.md)|Medium| | | 3 |[Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/3.%20Longest%20Substring%20Without%20Repeating%20Characters.md)|Medium| | +| 4 |[Median of Two Sorted Arrays](https://leetcode.com/problems/median-of-two-sorted-arrays/)|[C++](solutions/4.%20Median%20of%20Two%20Sorted%20Arrays.md)|Hard| | | 5 |[Longest Palindromic Substring](https://leetcode.com/problems/longest-palindromic-substring)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/5.%20Longest%20Palindromic%20Substring.md)|Medium| | | 6 |[ZigZag Conversion](https://leetcode.com/problems/zigzag-conversion)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/6.%20ZigZag%20Conversion.md)|Medium| | | 7 |[Reverse Integer](https://leetcode.com/problems/reverse-integer)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/7.%20Reverse%20Integer.md)|Easy| | diff --git a/solutions/4. Median of Two Sorted Arrays.md b/solutions/4. Median of Two Sorted Arrays.md new file mode 100644 index 0000000..e8e2256 --- /dev/null +++ b/solutions/4. Median of Two Sorted Arrays.md @@ -0,0 +1,189 @@ +# [4. Median of Two Sorted Arrays](https://leetcode.com/problems/median-of-two-sorted-arrays/) + +# 思路 + +给定两个(递增)排好序的数组,求所有数字的中位数。 +要求时间复杂度为O(log(m+n)),所以从头到尾合并两个数组直到遇到中位数的方式不可取,因为这样复杂度为O(m+n)。 + +## 思路一 + +核心思想:**按照中位数的定义将所有元素划分成两个大小相等(或差一)的集合。** + +排好序+对数复杂度这两个条件就相当于告诉我们要用二分的思路。 + +先看看中位数的定义:在统计学中,中位数用于 +* 将集合划分为两个相等长度的子集, +* 且一个子集总是大于另一个子集。 + +设给的两个数组分别是A和B,我们先随机将A划分成两个部分: + +``` + left_A | right_A +A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1] +``` +所以 i 有m+1中可能,即`0,1,...,m`。 + +同理,可将B分成两个部分: +``` + left_B | right_B +B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1] +``` +j 可以是`0,1,...,n`。 + +我们 left_A 和 left_B 放入同一个集合,将 right_A 和 right_B 放入另外一个集合: +``` + left_part | right_part +A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1] +B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1] +``` + +由于我们想求中位数,所以需要满足 +``` +1. len(left_part) == len(right_part) (或者相差1, 当m+n为奇数的时候) +2. max(left_part) <= min(right_part) <==> B[j-1] <= A[i] && A[i-1] <= B[j] +``` +对应于中位数的定义中的两个条件。 + +为了满足第一个条件,我们规定 +``` +若 m+n = 2N, 则 len(left_part) == N +若 m+n = 2N+1, 则 len(left_part) == N + 1 +所以这里的 N = (m+n+1)/2 + +又因为 len(left_part) == i + j +所以 j = N - i +``` + +为了使`j = N - i`在区间[0, n]内,我们规定**m <= n**。 + +第一个条件始终满足后,我们就可以利用第二个条件进行二分了,即在 [0, m] 中进行二分查找,使找到的 i 满足: +``` +B[j-1] <= A[i] && A[i-1] <= B[j], where j = N - i +``` + +设初始时`min_i = 0, max_i = m`,则算法步骤就是 +1. 设 `i = (min_i + max_i) / 2, j = N - i`; +2. 此时有三种情况: + * 若`B[j-1] <= A[i] && A[i-1] <= B[j]`, 即找到了所需的i,停止搜索; + * 若`B[j-1] > A[i]`, 意味着 A[i] 太小 B[j-1] 太大,那么我们需增大 i 。因为只有 i 增大时(j 会跟着减小)`B[j-1] <= A[i]`才可能成立。所以,更新`min_i = i+1`, 然后回到步骤1. + * 若`A[i-1] > B[j]`, 意味着 A[i-1] 太大 B[j] 太小。所以,更新`max_i = i-1`, 然后回到步骤1. + +上述算法步骤中我们没有考虑边界条件,写代码时需要仔细考虑,而且还需要考虑 m+n 为奇偶时的不同情况: +* 若为偶数,所以`len(left_part) == N`且`len(right_part) == N`,所以最终的中位数为`(max_of_left + min_of_right) / 2`。 +* 若为奇数,所以`len(left_part) == N+1`且`len(right_part) == N`,所以最终的中位数为`max_of_left`。 + +时间复杂度O(log(min(m, n))),空间复杂度O(1) + +[参考链接](https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-shu-b/) + +## 思路二、求第k小的数 + +核心思想:**每次去掉k/2个数,然后递归求第(k - k/2)小的数** + +此题更一般的问法是求第k小的数(设k从1编号),我们设为target。 + +我们设两个数组的第 k/2 大的数分别为 a 和 b, +* 若 a < b,那么target肯定比 a 大,所以我们可以排除掉第一个数组的前 k/2 个数,我们继续在剩下的元素中找递归找第 k - k/2 大的元素; +* 否则,即 a >= b,那么target肯定不比 b 小,所以我们可以排除掉第二个数组的前 k/2 个数,我们继续在剩下的元素中找递归找第 k - k/2 大的元素; + +举个例子来说明,假设题目 k = 7: + +