Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/top 100 week 1 #765

Merged
merged 8 commits into from
Aug 10, 2023
10 changes: 5 additions & 5 deletions leetcode-docs/easy/1-two-sum.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ keywords:
```ts
输入: nums = [2, 7, 11, 15], target = 9
输出: [0, 1]
解释: 因为 nums[0] + nums[1] == 9返回 [0, 1]
解释: 因为 nums[0] + nums[1] == 9, 返回 [0, 1]
```

```ts
输入: nums = [3, 2, 4], target = 6
输入: (nums = [3, 2, 4]), (target = 6)
输出: [1, 2]
```

```ts
输入: nums = [3, 3], target = 6
输入: (nums = [3, 3]), (target = 6)
输出: [0, 1]
```

Expand Down Expand Up @@ -103,5 +103,5 @@ pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {

## 复杂度分析

- 时间复杂度: O(n)
- 空间复杂度: O(n)
- 时间复杂度: `O(n)`
- 空间复杂度: `O(n)`
140 changes: 91 additions & 49 deletions leetcode-docs/hard/4-find-median-sorted-arrays.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ keywords:

## 题目

给定两个大小分别为 `m``n` 的正序数组 `nums1``nums2`. 请你找出并返回这两个正序数组的**中位数**.
给定两个大小分别为 `m``n` 的正序数组 `nums1``nums2`. 请你找出并返回这两个正序数组的**中位数**. 要求使用 `O(log(m + n))` 的时间复杂度.

:::note 提示:

Expand Down Expand Up @@ -49,7 +49,7 @@ import TabItem from '@theme/TabItem'
<Tabs>
<TabItem value="JavaScript - 朴素解法" label="JavaScript - 朴素解法">

最朴素的解法是将两个数组按照从小到大合并到一起, 然后取中值. 具体看代码注释.
最朴素的解法是将两个数组按照从小到大合并到一起, 然后取中值, 需要考虑合并后的数组长度是奇数个还是偶数个.

```ts
/**
Expand All @@ -61,11 +61,7 @@ var findMedianSortedArrays = function (nums1, nums2) {
const m = nums1.length
const n = nums2.length
const total = m + n
let isOdd = true

if (total % 2 === 0) {
isOdd = false
}
const isOdd = total % 2 === 1

const mid = (total / 2) | 0
const arr = []
Expand Down Expand Up @@ -103,33 +99,72 @@ var findMedianSortedArrays = function (nums1, nums2) {
</TabItem>
<TabItem value="JavaScript - 二分查找" label="JavaScript - 二分查找" default>

由于题目要求 `O(log(m + n))` 的时间复杂度, 那就要往二分查找上想. 题目要求求中位数, 其实就是求第 `k` 小的数. 举一个例子, 数组 a 是 `[1, 3, 4, 9]`,
数组 b 是 `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]`, 我们找第 7 小的数字.

我们找第 `k / 2` 个数, 如果 `k` 是奇数, 向下取整, 因此得到的 `k / 2``3`. 此时数组 a 第三个数是 `4`, 数组 b 第三个数是 `3`, 由于数组 b 的第三个数小于数组 a 第三个数,
所以数组 b 的前三个数, 也就是 `1`, `2`, `3` 一定不是第 `7` 小的数字.

此时, 我们去掉数组 b 的前三个数, 变成 `[4, 5, 6, 7, 8, 9, 10]`, 并且由于去掉了三个数, 那 `k` 也相应的变成了 `4`, 此时 `k / 2``2`.

同样的方法, 数组 a 的第二个数为 `3`, 数组 b 的第二个数为 `5`, 所以此时把数组 a 的前两个数去掉, 此时数组 a 变成 `[4, 9]`, 那 `k` 也相应的变成了 `2`, 此时 `k / 2``1`.

由于此时两个数组 a, b 的第一个数都是 4, 所以随便去掉一个就行, 比如我们把数组 b 的第一个元素干掉, 此时数组 b 变成 `[5, 6, 7, 8, 9, 10]`, 那 `k` 也相应的变成了 `1`.

这时候我们就可以停下来了, 因为 `k` 已经是 `1` 了, 我们就比较两个数组第一个元素哪个最小, 哪个就是第 `k` 小的元素, 所以最终答案为 `4`.

当然这里还有一种特殊情况, 比如数组 a 在递归的过程中空了, 但此时 `k``5`, 那就找数组 b 的第 `5` 个就好了.

当然在代码实践上, 我们并不是真正把数组前面的一截去掉, 而是通过指针来模拟起始位置.

```ts
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number}
*/
var findMedianSortedArrays = function (nums1, nums2) {
let len1 = nums1.length
let len2 = nums2.length
let left = Math.floor((len1 + len2 + 1) / 2)
let right = Math.floor((len1 + len2 + 2) / 2)
const m = nums1.length
const n = nums2.length

// 由于 k 的意义是第 k 个最小的数, 如果不加一就取成索引了.
const left = Math.floor((m + n + 1) / 2)
const right = Math.floor((m + n + 2) / 2)

// 求 left 与 right 之和是为了打平奇数长度和偶数长度, 由于最后算了两次, 所以最终结果要除以 2
return (
(findkth(nums1, 0, len1 - 1, nums2, 0, len2 - 1, left) +
findkth(nums1, 0, len1 - 1, nums2, 0, len2 - 1, right)) /
(findkth(nums1, 0, m - 1, nums2, 0, n - 1, left) +
findkth(nums1, 0, m - 1, nums2, 0, n - 1, right)) /
2
)
}

var findkth = function (arr1, start1, end1, arr2, start2, end2, k) {
let n = end1 - start1 + 1
let m = end2 - start2 + 1
// 获取两个数组的长度
const m = end1 - start1 + 1
const n = end2 - start2 + 1

if (n > m) return findkth(arr2, start2, end2, arr1, start1, end1, k)
if (n === 0) return arr2[start2 + k - 1]
// 如果数组 a 空了, 那最终答案就在数组 b 中寻找, 即 arr2[start2 + k - 1]
if (m === 0) return arr2[start2 + k - 1]

// 如果数组 b 空了, 那最终答案就在数组 a 中寻找, 即 arr1[start1 + k - 1]
if (n === 0) return arr1[start1 + k - 1]

// 如果 k === 1, 就说明两个数组的第一个元素中, 最小的那个就是答案
if (k === 1) return Math.min(arr1[start1], arr2[start2])

let i = start1 + Math.min(n, Math.floor(k / 2)) - 1
let j = start2 + Math.min(m, Math.floor(k / 2)) - 1
// 每次让数组长度(m 或 n) 与 Math.floor(k / 2) 比较, 取最小的那个
// 这样的目的是如果 Math.floor(k / 2) 比数组的长度大了, 如果去 Math.floor(k / 2) 的话, 数组就越界了
// 因此需要取两者中最小的, 就保证 i 或者 j 在这种情况就指到了数组的最后一个元素(下一次递归时这个数组长度就为 0 了)
const i = start1 + Math.min(m, Math.floor(k / 2)) - 1
const j = start2 + Math.min(n, Math.floor(k / 2)) - 1

if (arr1[i] > arr2[j]) {
// 如果 arr1[i] > arr2[j], 说明要把 arr2[j] 前 j 个干掉, 即把 start2 设为 j + 1,
// 此外由于数组 2 被削减了 j - start2 + 1 个, 所以 k 变成 k - (j - start2 + 1)
return findkth(arr1, start1, end1, arr2, j + 1, end2, k - (j - start2 + 1))
} else {
// 同理
return findkth(arr1, i + 1, end1, arr2, start2, end2, k - (i - start1 + 1))
}
}
Expand All @@ -142,47 +177,54 @@ var findkth = function (arr1, start1, end1, arr2, start2, end2, k) {
<TabItem value="Rust" label="Rust">

```rust
pub fn find_median_sorted_arrays(nums1: Vec<i32>, nums2: Vec<i32>) -> f64 {
let (m, n) = (nums1.len(), nums2.len());
let mut is_odd = true;
let mut total = m + n;
use std::cmp;

if total % 2 == 0 {
is_odd = false;
}

total /= 2;
pub fn find_median_sorted_arrays(nums1: Vec<i32>, nums2: Vec<i32>) -> f64 {
let m = nums1.len();
let n = nums2.len();

let mut arr = vec![];
let left = (m + n + 1) / 2;
let right = (m + n + 2) / 2;

let (mut i, mut j) = (0, 0);
(find_kth(&nums1, 0, m - 1, &nums2, 0, n - 1, left)
+ find_kth(&nums1, 0, m - 1, &nums2, 0, n - 1, right))
/ 2.0
}

while i + j <= total {
if i == m {
arr.append(&mut nums2[j..].to_vec());
break;
}
fn find_kth(
arr1: &Vec<i32>,
start1: usize,
end1: usize,
arr2: &Vec<i32>,
start2: usize,
end2: usize,
k: usize,
) -> f64 {
let m = end1 - start1 + 1;
let n = end2 - start2 + 1;

if m == 0 {
return arr2[start2 + k - 1].into();
}

if j == n {
arr.append(&mut nums1[i..].to_vec());
break;
}
if n == 0 {
return arr1[start1 + k - 1].into();
}

if nums1[i] < nums2[j] {
arr.push(nums1[i]);
i += 1;
} else {
arr.push(nums2[j]);
j += 1;
}
if k == 1 {
return cmp::min(arr1[start1], arr2[start2]).into();
}

if is_odd {
arr[total] as f64
let i = start1 + cmp::min(m, k / 2) - 1;
let j = start2 + cmp::min(n, k / 2) - 1;

if arr1[i] > arr2[j] {
return find_kth(arr1, start1, end1, arr2, j + 1, end2, k - (j - start2 + 1));
} else {
(arr[total] + arr[total - 1]) as f64 / 2.0
return find_kth(arr1, i + 1, end1, arr2, start2, end2, k - (i - start1 + 1));
}
}

```

</TabItem>
Expand Down
67 changes: 64 additions & 3 deletions leetcode-docs/medium/2-add-two-numbers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ keywords:

## 题解

import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'

<Tabs>
<TabItem value="JavaScript" label="JavaScript" default>

```ts
/**
* Definition for singly-linked list.
Expand All @@ -53,9 +59,9 @@ var addTwoNumbers = function (l1, l2) {

// 将 l1 和 l2 都走到头
while (l1 || l2) {
const x = l1 ? l1.val : 0
const y = l2 ? l2.val : 0
const sum = x + y + carry
const a = l1 ? l1.val : 0
const b = l2 ? l2.val : 0
const sum = a + b + carry

carry = (sum / 10) | 0
curr.next = new ListNode(sum % 10)
Expand All @@ -71,3 +77,58 @@ var addTwoNumbers = function (l1, l2) {
return dummy.next
}
```

</TabItem>

<TabItem value="Rust" label="Rust">

```rust
// Definition for singly-linked list.
// #[derive(PartialEq, Eq, Clone, Debug)]
// pub struct ListNode {
// pub val: i32,
// pub next: Option<Box<ListNode>>
// }
//
// impl ListNode {
// #[inline]
// fn new(val: i32) -> Self {
// ListNode {
// next: None,
// val
// }
// }
// }
pub fn add_two_numbers(
l1: Option<Box<ListNode>>,
l2: Option<Box<ListNode>>,
) -> Option<Box<ListNode>> {
let mut dummy_head = ListNode::new(0);
let mut curr = &mut dummy_head;
let mut carry = 0;
let mut l1 = l1;
let mut l2 = l2;

while l1.is_some() || l2.is_some() || carry != 0 {
let mut sum = carry;

if let Some(node) = l1 {
sum += node.val;
l1 = node.next;
}
if let Some(node) = l2 {
sum += node.val;
l2 = node.next;
}

carry = sum / 10;
curr.next = Some(Box::new(ListNode::new(sum % 10)));
curr = curr.next.as_mut().unwrap();
}

dummy_head.next
}
```

</TabItem>
</Tabs>
30 changes: 14 additions & 16 deletions leetcode-docs/medium/3-length-of-longest-substring.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,17 @@ var lengthOfLongestSubstring = function (s) {
let max = 0

while (end < n) {
const endLetter = s[end++]
const endChar = s[end++]

if (map.has(endLetter)) {
map.set(endLetter, map.get(endLetter) + 1)
if (map.has(endChar)) {
map.set(endChar, map.get(endChar) + 1)
} else {
map.set(endLetter, 1)
map.set(endChar, 1)
}

while (map.get(endLetter) > 1) {
const startLetter = s[start++]
map.set(startLetter, map.get(startLetter) - 1)
while (map.get(endChar) > 1) {
const startChar = s[start++]
map.set(startChar, map.get(startChar) - 1)
}

max = Math.max(max, end - start)
Expand All @@ -123,16 +123,14 @@ pub fn length_of_longest_substring(s: String) -> i32 {
let mut end = 0;

while end < s.len() {
let end_letter = s_bytes[end];
let end_char = s_bytes[end];
end += 1;
map.entry(end_letter).and_modify(|e| *e += 1).or_insert(1);
map.entry(end_char).and_modify(|e| *e += 1).or_insert(1);

// while map.get(&end_letter).unwrap() > &1 {
// while map.get(&end_letter) > Some(&1) {
while map[&end_letter] > 1 {
let start_letter = s_bytes[start];
while map[&end_char] > 1 {
let start_char = s_bytes[start];
start += 1;
map.entry(start_letter).and_modify(|e| *e -= 1);
map.entry(start_char).and_modify(|e| *e -= 1);
}

max = cmp::max(end - start, max);
Expand All @@ -148,5 +146,5 @@ pub fn length_of_longest_substring(s: String) -> i32 {

## 复杂度分析

- 时间复杂度: O(n)
- 空间复杂度: O(n)
- 时间复杂度: `O(n)`
- 空间复杂度: `O(n)`
Loading
Loading