Skip to content

Commit

Permalink
Merge pull request #765 from YanceyOfficial/feat/top-100-week-1
Browse files Browse the repository at this point in the history
Feat/top 100 week 1
  • Loading branch information
YanceyOfficial authored Aug 10, 2023
2 parents dc9c1e7 + 8aaf021 commit c5b041c
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 134 deletions.
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

0 comments on commit c5b041c

Please sign in to comment.