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
2 changes: 1 addition & 1 deletion Solutions/0015. 三数之和.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

直接三重遍历查找 $a$、$b$、$c$ 的时间复杂度是:$O(n^3)$。我们可以通过一些操作来降低复杂度。

先将数组进行排序,以保证按顺序查找 $a$、$b$、$c$ 时,元素值为升序,从而保证所找到的三个元素是不重复的。同时也方便下一步使用双指针减少一重遍历。时间复杂度为:$O(nlogn)$。
先将数组进行排序,以保证按顺序查找 $a$、$b$、$c$ 时,元素值为升序,从而保证所找到的三个元素是不重复的。同时也方便下一步使用双指针减少一重遍历。时间复杂度为:$O(n \times \log n)$。

第一重循环遍历 $a$,对于每个 $a$ 元素,从 $a$ 元素的下一个位置开始,使用对撞指针 $left$,$right$。$left$ 指向 $a$ 元素的下一个位置,$right$ 指向末尾位置。先将 $left$ 右移、$right$ 左移去除重复元素,再进行下边的判断。

Expand Down
52 changes: 42 additions & 10 deletions Solutions/0018. 四数之和.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,51 @@

## 题目大意

给定一个整数数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a、b、c、d,使得 a + b + c + d = target。要求找出所有满足条件且不重复的四元组
**描述**:给定一个整数数组 $nums$ 和一个目标值 $target$

## 解题思路
**要求**:找出所有满足以下条件切不重复的四元组。

和 [0015. 三数之和](https://leetcode.cn/problems/3sum/) 解法类似。
1. $0 \le a, b, c, d < n$。
2. $a$、$b$、$c$ 和 $d$ 互不相同。
3. $nums[a] + nums[b] + nums[c] + nums[d] == target$。

直接三重遍历查找 a、b、c、d 的时间复杂度是:$O(n^4)$。我们可以通过一些操作来降低复杂度。
**说明**:

先将数组进行排序,以保证按顺序查找 a、b、c、d 时,元素值为升序,从而保证所找到的四个元素是不重复的。同时也方便下一步使用双指针减少一重遍历。时间复杂度为:$O(nlogn)$
- $1 \le nums.length \le 200$。
- $-10^9 \le nums[i] \le 10^9$。
- $-10^9 \le target \le 10^9$。

两重循环遍历元素 a、b,对于每个 a 元素,从 a 元素的下一个位置开始遍历元素 b。对于元素 a、b,使用双指针 left,right 来查找 c、d。left 指向 b 元素的下一个位置,right 指向末尾位置。先将 left 右移、right 左移去除重复元素,再进行下边的判断。
**示例**:

- 若 `nums[a] + nums[b] + nums[left] + nums[right] = target`,则得到一个解,将其加入答案数组中,并继续将 left 右移,right 左移;
- 示例 1:

- 若 `nums[a] + nums[b] + nums[left] + nums[right] > target`,说明 nums[right] 值太大,将 right 向左移;
- 若 `nums[a] + nums[b] + nums[left] + nums[right] < target`,说明 nums[left] 值太小,将 left 右移。
```python
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
```

## 代码
- 示例 2:

```python
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
```

## 解题思路

### 思路 1:排序 + 双指针

和 [0015. 三数之和](https://leetcode.cn/problems/3sum/) 解法类似。

直接三重遍历查找 $a$、$b$、$c$、$d$ 的时间复杂度是:$O(n^4)$。我们可以通过一些操作来降低复杂度。

1. 先将数组进行排序,以保证按顺序查找 $a$、$b$、$c$、$d$ 时,元素值为升序,从而保证所找到的四个元素是不重复的。同时也方便下一步使用双指针减少一重遍历。这一步的时间复杂度为:$O(n \times \log n)$。
2. 两重循环遍历元素 $a$、$b$,对于每个 $a$ 元素,从 $a$ 元素的下一个位置开始遍历元素 $b$。对于元素 $a$、$b$,使用双指针 $left$,$right$ 来查找 $c$、$d$。$left$ 指向 $b$ 元素的下一个位置,$right$ 指向末尾位置。先将 $left$ 右移、$right$ 左移去除重复元素,再进行下边的判断。
1. 如果 $nums[a] + nums[b] + nums[left] + nums[right] == target$,则得到一个解,将其加入答案数组中,并继续将 $left$ 右移,$right$ 左移;
2. 如果 $nums[a] + nums[b] + nums[left] + nums[right] > target$,说明 $nums[right]$ 值太大,将 $right$ 向左移;
3. 如果 $nums[a] + nums[b] + nums[left] + nums[right] < target$,说明 $nums[left]$ 值太小,将 $left$ 右移。

### 思路 1:代码

```python
class Solution:
Expand Down Expand Up @@ -55,3 +82,8 @@ class Solution:
return ans
```

### 思路 1:复杂度分析

- **时间复杂度**:$O(n^3)$,其中 $n$ 为数组中元素个数。
- **空间复杂度**:$O(\log n)$,排序额外使用空间为 $\log n$。

2 changes: 1 addition & 1 deletion Solutions/0350. 两个数组的交集 II.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

## 题目大意

**描述**:给定两个数组 `nums1``nums2`
**描述**:给定两个数组 $nums1$$nums2$

**要求**:返回两个数组的交集。可以不考虑输出结果的顺序。

Expand Down
49 changes: 38 additions & 11 deletions Solutions/0383. 赎金信.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,47 @@

## 题目大意

为了不在赎金信中暴露字迹,从杂志上搜索各个需要的字母,组成单词来表达意思。
**描述**:为了不在赎金信中暴露字迹,从杂志上搜索各个需要的字母,组成单词来表达意思。

给定一个赎金信字符串 `ransomNote` 和一个杂志字符串 `magazine`
给定一个赎金信字符串 $ransomNote$ 和一个杂志字符串 $magazine$

要求:判断 `ransomNote` 能不能由 `magazines` 里面的字符构成。如果可以构成,返回 `True`;否则返回 `False`。
**要求**:判断 $ransomNote$ 能不能由 $magazines$ 里面的字符构成。如果可以构成,返回 `True`;否则返回 `False`。

注意:`magazine` 中的每个字符只能在 `ransomNote` 中使用一次。
**说明**:

- $magazine$ 中的每个字符只能在 $ransomNote$ 中使用一次。
- $1 \le ransomNote.length, magazine.length \le 10^5$。
- $ransomNote$ 和 $magazine$ 由小写英文字母组成。

**示例**:

- 示例 1:

```python
输入:ransomNote = "a", magazine = "b"
输出:False
```

- 示例 2:

```python
输入:ransomNote = "aa", magazine = "ab"
输出:False
```

## 解题思路

暴力做法是双重循环遍历字符串 `ransomNote` 和 `magazine`。我们可以用哈希表来减少算法的时间复杂度。具体做法如下:
### 思路 1:哈希表

- 先用哈希表存储 `magazine` 中各个字符的个数(哈希表可用字典或数组实现)。
- 再遍历字符串 `ransomNote` 中每个字符,对于每个字符:
- 如果在哈希表中个数为 `0`,直接返回 `False`。
- 如果在哈希表中个数不为 `0`,将其个数减 1。
- 遍历到最后,则说明 `ransomNote` 能由 `magazines` 里面的字符构成。返回 `True`。
暴力做法是双重循环遍历字符串 $ransomNote$ 和 $magazines$。我们可以用哈希表来减少算法的时间复杂度。具体做法如下:

## 代码
- 先用哈希表存储 $magazines$ 中各个字符的个数(哈希表可用字典或数组实现)。
- 再遍历字符串 $ransomNote$ 中每个字符,对于每个字符:
- 如果在哈希表中个数为 $0$,直接返回 `False`。
- 如果在哈希表中个数不为 $0$,将其个数减 $1$。
- 遍历到最后,则说明 $ransomNote$ 能由 $magazines$ 里面的字符构成。返回 `True`。

### 思路 1:代码

```python
class Solution:
Expand All @@ -44,3 +66,8 @@ class Solution:
return True
```

### 思路 1:复杂度分析

- **时间复杂度**:$O(m + n)$,其中 $m$ 是字符串 $ransomNote$ 的长度,$n$ 是字符串 $magazines$ 的长度。
- **空间复杂度**:$O(|S|)$,其中 $S$ 是字符集,本题中 $|S| = 26$。

67 changes: 55 additions & 12 deletions Solutions/0399. 除法求值.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,66 @@

## 题目大意

给定一个变量对数组 `equations` 和一个实数数组 `values` 作为已知条件,其中 `equations[i] = [Ai, Bi]``values[i]` 共同表示 `Ai / Bi = values[i]`。每个 `Ai``Bi` 是一个表示单个变量的字符串。
**描述**:给定一个变量对数组 $equations$ 和一个实数数组 $values$ 作为已知条件,其中 $equations[i] = [Ai, Bi]$$values[i]$ 共同表示 `Ai / Bi = values[i]`。每个 $Ai$$Bi$ 是一个表示单个变量的字符串。

再给定一个表示多个问题的数组 `queries`,其中 `queries[j] = [Cj, Dj]` 表示第 `j` 个问题,要求:根据已知条件找出 `Cj / Dj = ?` 的结果作为答案。返回所有问题的答案。如果某个答案无法确定,则用 `-1.0` 代替,如果问题中出现了给定的已知条件中没有出现的表示变量的字符串,则也用 `-1.0` 代替这个答案。
再给定一个表示多个问题的数组 $queries$,其中 $queries[j] = [Cj, Dj]$ 表示第 $j$ 个问题,要求:根据已知条件找出 `Cj / Dj = ?` 的结果作为答案。

**要求**:返回所有问题的答案。如果某个答案无法确定,则用 $-1.0$ 代替,如果问题中出现了给定的已知条件中没有出现的表示变量的字符串,则也用 $-1.0$ 代替这个答案。

**说明**:

- 未在等式列表中出现的变量是未定义的,因此无法确定它们的答案。
- $1 \le equations.length \le 20$。
- $equations[i].length == 2$。
- $1 \le Ai.length, Bi.length \le 5$。
- $values.length == equations.length$。
- $0.0 < values[i] \le 20.0$。
- $1 \le queries.length \le 20$。
- $queries[i].length == 2$。
- $1 \le Cj.length, Dj.length \le 5$。
- $Ai, Bi, Cj, Dj$ 由小写英文字母与数字组成。

**示例**:

- 示例 1:

```python
输入:equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
输出:[6.00000,0.50000,-1.00000,1.00000,-1.00000]
解释:
条件:a / b = 2.0, b / c = 3.0
问题:a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ?
结果:[6.0, 0.5, -1.0, 1.0, -1.0 ]
注意:x 是未定义的 => -1.0
```

- 示例 2:

```python
输入:equations = [["a","b"],["b","c"],["bc","cd"]], values = [1.5,2.5,5.0], queries = [["a","c"],["c","b"],["bc","cd"],["cd","bc"]]
输出:[3.75000,0.40000,5.00000,0.20000]
```

## 解题思路

### 思路 1:并查集

在「[等式方程的可满足性](https://leetcode.cn/problems/satisfiability-of-equality-equations)」的基础上增加了倍数关系。在「[等式方程的可满足性](https://leetcode.cn/problems/satisfiability-of-equality-equations)」中我们处理传递关系使用了并查集,这道题也是一样,不过在使用并查集的同时还要维护倍数关系。

举例说明:

- `a / b = 2.0`:说明 `a = 2b`,`a``b` 在同一个集合。
- `b / c = 3.0`:说明 `b = 3c`,`b``c` 在同一个集合。
- `a / b = 2.0`:说明 $a == 2b$,$a$$b$ 在同一个集合。
- `b / c = 3.0`:说明 $b == 3c$,$b$$c$ 在同一个集合。

根据上述两式可得:`a`、`b`、`c` 都在一个集合中,且 `a = 2b = 6c`
根据上述两式可得:$a$、$b$、$c$ 都在一个集合中,且 $a == 2b == 6c$

我们可以将同一集合中的变量倍数关系都转换为与根节点变量的倍数关系,比如上述例子中都转变为与 `a` 的倍数关系。
我们可以将同一集合中的变量倍数关系都转换为与根节点变量的倍数关系,比如上述例子中都转变为与 $a$ 的倍数关系。

具体操作如下:

- 定义并查集结构,并在并查集中定义一个表示倍数关系的 `multiples` 数组。
- 遍历 `equations` 数组、`values` 数组,将每个变量按顺序编号,并使用 `union` 将其并入相同集合。
- 遍历 `queries` 数组,判断两个变量是否在并查集中,并且是否在同一集合。如果找到对应关系,则将计算后的倍数关系存入答案数组,否则则将 `-1` 存入答案数组。
- 定义并查集结构,并在并查集中定义一个表示倍数关系的 $multiples$ 数组。
- 遍历 $equations$ 数组、$values$ 数组,将每个变量按顺序编号,并使用 `union` 将其并入相同集合。
- 遍历 $queries$ 数组,判断两个变量是否在并查集中,并且是否在同一集合。如果找到对应关系,则将计算后的倍数关系存入答案数组,否则则将 $-1$ 存入答案数组。
- 最终输出答案数组。

并查集中维护倍数相关方法说明:
Expand All @@ -36,11 +74,11 @@
- `union` 方法:
- 如果两个节点属于同一集合,则直接返回。
- 如果两个节点不属于同一个集合,合并之前当前节点的倍数关系更新,然后再进行更新。
- `is_connect` 方法:
- 如果两个节点不属于同一集合,返回 `-1`
- `is_connected` 方法:
- 如果两个节点不属于同一集合,返回 $-1$
- 如果两个节点属于同一集合,则返回倍数关系。

## 代码
### 思路 1:代码

```python
class UnionFind:
Expand Down Expand Up @@ -109,3 +147,8 @@ class Solution:
return res
```

### 思路 1:复杂度分析

- **时间复杂度**:$O((m + n) \times \alpha(m + n))$,$\alpha$ 是反 `Ackerman` 函数。
- **空间复杂度**:$O(m + n)$。

51 changes: 46 additions & 5 deletions Solutions/0454. 四数相加 II.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,55 @@

## 题目大意

给定四个整数数组 nums1、nums2、nums3、nums4。计算有多少不同的(i, j, k, l)满足 nums1[i] + nums2[j] + nums3[k] + nums4[l] = 0。
**描述**:给定四个整数数组 $nums1$、$nums2$、$nums3$、$nums4$。

**要求**:计算有多少不同的 $(i, j, k, l)$ 满足以下条件。

1. $0 \le i, j, k, l < n$。
2. $nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0$。

**说明**:

- $n == nums1.length$。
- $n == nums2.length$。
- $n == nums3.length$。
- $n == nums4.length$。
- $1 \le n \le 200$。
- $-2^{28} \le nums1[i], nums2[i], nums3[i], nums4[i] \le 2^{28}$。

**示例**:

- 示例 1:

```python
输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
```

- 示例 2:

```python
输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1
```

## 解题思路

### 思路 1:哈希表

直接暴力搜索的时间复杂度是 $O(n^4)$。我们可以降低一下复杂度。

将四个数组分为两组。nums1 和 nums2 分为一组,nums3 和 nums4 分为一组。
将四个数组分为两组。$nums1$$nums2$ 分为一组,$nums3$$nums4$ 分为一组。

已知 $nums1[i] + nums2[j] + nums3[k] + nums4[l] = 0$,可以得到 $nums1[i] + nums2[j] = -(nums3[k] + nums4[l])$
已知 $nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0$,可以得到 $nums1[i] + nums2[j] = -(nums3[k] + nums4[l])$

建立一个哈希表。两重循环遍历数组 nums1nums2,先将 $nums[i] + nums[j]$ 的和个数记录到哈希表中,然后再用两重循环遍历数组 nums3nums4。如果 $-(nums3[k] + nums4[l])$ 的结果出现在哈希表中,则将结果数累加到答案中。最终输出累加之后的答案。
建立一个哈希表。两重循环遍历数组 $nums1$、$nums2$,先将 $nums[i] + nums[j]$ 的和个数记录到哈希表中,然后再用两重循环遍历数组 $nums3$、$nums4$。如果 $-(nums3[k] + nums4[l])$ 的结果出现在哈希表中,则将结果数累加到答案中。最终输出累加之后的答案。

## 代码
### 思路 1:代码

```python
class Solution:
Expand All @@ -40,3 +76,8 @@ class Solution:
return count
```

### 思路 1:复杂度分析

- **时间复杂度**:$O(n^2)$,其中 $n$ 为数组的元素个数。
- **空间复杂度**:$O(n^2)$。

51 changes: 43 additions & 8 deletions Solutions/0705. 设计哈希集合.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,51 @@

## 题目大意

要求不使用内建的哈希表库,自行实现一个哈希集合(HashSet)。
**要求**:不使用内建的哈希表库,自行实现一个哈希集合(HashSet)。

满足以下操作
需要满足以下操作

- void add(key) 向哈希集合中插入值 key 。
- bool contains(key) 返回哈希集合中是否存在这个值 key 。
- void remove(key) 将给定值 key 从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。
- `void add(key)` 向哈希集合中插入值 $key$。
- `bool contains(key)` 返回哈希集合中是否存在这个值 $key$。
- `void remove(key)` 将给定值 $key$ 从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。

**说明**:

- $0 \le key \le 10^6$。
- 最多调用 $10^4$ 次 `add`、`remove` 和 `contains`。

**示例**:

- 示例 1:

```python
输入:
["MyHashSet", "add", "add", "contains", "contains", "add", "contains", "remove", "contains"]
[[], [1], [2], [1], [3], [2], [2], [2], [2]]
输出:
[null, null, null, true, false, null, true, null, false]

解释:
MyHashSet myHashSet = new MyHashSet();
myHashSet.add(1); // set = [1]
myHashSet.add(2); // set = [1, 2]
myHashSet.contains(1); // 返回 True
myHashSet.contains(3); // 返回 False ,(未找到)
myHashSet.add(2); // set = [1, 2]
myHashSet.contains(2); // 返回 True
myHashSet.remove(2); // set = [1]
myHashSet.contains(2); // 返回 False ,(已移除)
```

## 解题思路

可以利用「数组+链表」的方式实现哈希集合。
### 思路 1:数组 + 链表

定义一个一维长度为 $buckets$ 的二维数组 $table$。

定义一个一维长度为 buckets 的二维数组 table。第一维度用于计算哈希函数,为 key 分桶。第二个维度用于寻找 key 存放的具体位置。第二维度的数组会根据 key 值动态增长,模拟真正的链表。
第一维度用于计算哈希函数,为 $key$ 进行分桶。第二个维度用于寻找 $key$ 存放的具体位置。第二维度的数组会根据 $key$ 值动态增长,模拟真正的链表。

## 代码
### 思路 1:代码

```python
class MyHashSet:
Expand Down Expand Up @@ -52,3 +82,8 @@ class MyHashSet:
return key in self.table[hash_key]
```

### 思路 1:复杂度分析

- **时间复杂度**:$O(\frac{n}{m})$,其中 $n$ 为哈希表中的元素数量,$b$ 为 $table$ 的元素个数,也就是链表的数量。
- **空间复杂度**:$O(n + m)$。

Loading