diff --git "a/Solutions/0015. \344\270\211\346\225\260\344\271\213\345\222\214.md" "b/Solutions/0015. \344\270\211\346\225\260\344\271\213\345\222\214.md" index ed5e3df7..7d80dbca 100644 --- "a/Solutions/0015. \344\270\211\346\225\260\344\271\213\345\222\214.md" +++ "b/Solutions/0015. \344\270\211\346\225\260\344\271\213\345\222\214.md" @@ -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$ 左移去除重复元素,再进行下边的判断。 diff --git "a/Solutions/0018. \345\233\233\346\225\260\344\271\213\345\222\214.md" "b/Solutions/0018. \345\233\233\346\225\260\344\271\213\345\222\214.md" index e2d57363..b68dbd51 100644 --- "a/Solutions/0018. \345\233\233\346\225\260\344\271\213\345\222\214.md" +++ "b/Solutions/0018. \345\233\233\346\225\260\344\271\213\345\222\214.md" @@ -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: @@ -55,3 +82,8 @@ class Solution: return ans ``` +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(n^3)$,其中 $n$ 为数组中元素个数。 +- **空间复杂度**:$O(\log n)$,排序额外使用空间为 $\log n$。 + diff --git "a/Solutions/0350. \344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206 II.md" "b/Solutions/0350. \344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206 II.md" index 29f04591..172f6c98 100644 --- "a/Solutions/0350. \344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206 II.md" +++ "b/Solutions/0350. \344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206 II.md" @@ -5,7 +5,7 @@ ## 题目大意 -**描述**:给定两个数组 `nums1` 和 `nums2`。 +**描述**:给定两个数组 $nums1$ 和 $nums2$。 **要求**:返回两个数组的交集。可以不考虑输出结果的顺序。 diff --git "a/Solutions/0383. \350\265\216\351\207\221\344\277\241.md" "b/Solutions/0383. \350\265\216\351\207\221\344\277\241.md" index 8c1043cb..0c761f1f 100644 --- "a/Solutions/0383. \350\265\216\351\207\221\344\277\241.md" +++ "b/Solutions/0383. \350\265\216\351\207\221\344\277\241.md" @@ -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: @@ -44,3 +66,8 @@ class Solution: return True ``` +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(m + n)$,其中 $m$ 是字符串 $ransomNote$ 的长度,$n$ 是字符串 $magazines$ 的长度。 +- **空间复杂度**:$O(|S|)$,其中 $S$ 是字符集,本题中 $|S| = 26$。 + diff --git "a/Solutions/0399. \351\231\244\346\263\225\346\261\202\345\200\274.md" "b/Solutions/0399. \351\231\244\346\263\225\346\261\202\345\200\274.md" index af543ba3..b696edcc 100644 --- "a/Solutions/0399. \351\231\244\346\263\225\346\261\202\345\200\274.md" +++ "b/Solutions/0399. \351\231\244\346\263\225\346\261\202\345\200\274.md" @@ -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$ 存入答案数组。 - 最终输出答案数组。 并查集中维护倍数相关方法说明: @@ -36,11 +74,11 @@ - `union` 方法: - 如果两个节点属于同一集合,则直接返回。 - 如果两个节点不属于同一个集合,合并之前当前节点的倍数关系更新,然后再进行更新。 -- `is_connect` 方法: - - 如果两个节点不属于同一集合,返回 `-1`。 +- `is_connected` 方法: + - 如果两个节点不属于同一集合,返回 $-1$。 - 如果两个节点属于同一集合,则返回倍数关系。 -## 代码 +### 思路 1:代码 ```python class UnionFind: @@ -109,3 +147,8 @@ class Solution: return res ``` +### 思路 1:复杂度分析 + +- **时间复杂度**:$O((m + n) \times \alpha(m + n))$,$\alpha$ 是反 `Ackerman` 函数。 +- **空间复杂度**:$O(m + n)$。 + diff --git "a/Solutions/0454. \345\233\233\346\225\260\347\233\270\345\212\240 II.md" "b/Solutions/0454. \345\233\233\346\225\260\347\233\270\345\212\240 II.md" index dee9d5b3..3d48cdff 100644 --- "a/Solutions/0454. \345\233\233\346\225\260\347\233\270\345\212\240 II.md" +++ "b/Solutions/0454. \345\233\233\346\225\260\347\233\270\345\212\240 II.md" @@ -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])$ -建立一个哈希表。两重循环遍历数组 nums1、nums2,先将 $nums[i] + nums[j]$ 的和个数记录到哈希表中,然后再用两重循环遍历数组 nums3、nums4。如果 $-(nums3[k] + nums4[l])$ 的结果出现在哈希表中,则将结果数累加到答案中。最终输出累加之后的答案。 +建立一个哈希表。两重循环遍历数组 $nums1$、$nums2$,先将 $nums[i] + nums[j]$ 的和个数记录到哈希表中,然后再用两重循环遍历数组 $nums3$、$nums4$。如果 $-(nums3[k] + nums4[l])$ 的结果出现在哈希表中,则将结果数累加到答案中。最终输出累加之后的答案。 -## 代码 +### 思路 1:代码 ```python class Solution: @@ -40,3 +76,8 @@ class Solution: return count ``` +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(n^2)$,其中 $n$ 为数组的元素个数。 +- **空间复杂度**:$O(n^2)$。 + diff --git "a/Solutions/0705. \350\256\276\350\256\241\345\223\210\345\270\214\351\233\206\345\220\210.md" "b/Solutions/0705. \350\256\276\350\256\241\345\223\210\345\270\214\351\233\206\345\220\210.md" index b0aa8c9c..cfb4f0d3 100644 --- "a/Solutions/0705. \350\256\276\350\256\241\345\223\210\345\270\214\351\233\206\345\220\210.md" +++ "b/Solutions/0705. \350\256\276\350\256\241\345\223\210\345\270\214\351\233\206\345\220\210.md" @@ -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: @@ -52,3 +82,8 @@ class MyHashSet: return key in self.table[hash_key] ``` +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(\frac{n}{m})$,其中 $n$ 为哈希表中的元素数量,$b$ 为 $table$ 的元素个数,也就是链表的数量。 +- **空间复杂度**:$O(n + m)$。 + diff --git "a/Solutions/0959. \347\224\261\346\226\234\346\235\240\345\210\222\345\210\206\345\214\272\345\237\237.md" "b/Solutions/0959. \347\224\261\346\226\234\346\235\240\345\210\222\345\210\206\345\214\272\345\237\237.md" index 1c9a60cc..dc78812a 100644 --- "a/Solutions/0959. \347\224\261\346\226\234\346\235\240\345\210\222\345\210\206\345\214\272\345\237\237.md" +++ "b/Solutions/0959. \347\224\261\346\226\234\346\235\240\345\210\222\345\210\206\345\214\272\345\237\237.md" @@ -5,39 +5,69 @@ ## 题目大意 -在由 `1 * 1` 方格组成的 `n * n` 网格 `grid` 中,每个 `1 * 1` 方块由 `/`、`\` 或空格构成。这些字符会将方块划分为一些共边的区域。 +**描述**:在由 $1 \times 1$ 方格组成的 $n \times n$ 网格 $grid$ 中,每个 $1 \times 1$ 方块由 `'/'`、`'\'` 或 `' '` 构成。这些字符会将方块划分为一些共边的区域。 -注意:反斜杠字符是转义的,因此 `\` 用 `\\` 表示。 +现在给定代表网格的二维数组 $grid$。 -现在给定代表网格的二维数组 `grid`,要求:返回区域的数目。 +**要求**:返回区域的数目。 + +**说明**: + +- 反斜杠字符是转义的,因此 `'\'` 用 `'\\'` 表示。 +- $n == grid.length == grid[i].length$。 +- $1 \le n \le 30$。 +- $grid[i][j]$ 是 `'/'`、`'\'` 或 `' '`。 + +**示例**: + +- 示例 1: + +![](https://assets.leetcode.com/uploads/2018/12/15/1.png) + +```python +输入:grid = [" /","/ "] +输出:2 +``` + +- 示例 2: + +![](https://assets.leetcode.com/uploads/2018/12/15/4.png) + +```python +输入:grid = ["/\\","\\/"] +输出:5 +解释:回想一下,因为 \ 字符是转义的,所以 "/\\" 表示 /\,而 "\\/" 表示 \/。 +``` ## 解题思路 -我们把一个 `1 * 1` 的单元格分割成逻辑上的 4 个部分,则 空格、`/`、`\\` 可以将 `1 * 1` 的方格分割为以下三种形态: +### 思路 1:并查集 + +我们把一个 $1 \times 1$ 的单元格分割成逻辑上的 $4$ 个部分,则 `' '`、`'/'`、`'\'` 可以将 $1 \times 1$ 的方格分割为以下三种形态: ![](http://qcdn.itcharge.cn/images/20210827142447.png) 在进行遍历的时候,需要将联通的部分进行合并,并统计出联通的块数。这就需要用到了并查集。 -遍历二维数组 `gird`,然后在「单元格内」和「单元格间」进行合并。 +遍历二维数组 $gird$,然后在「单元格内」和「单元格间」进行合并。 现在我们为单元格的每个小三角部分按顺时针方向都编上编号,起始位置为左边。然后单元格间的编号按照从左到右,从上到下的位置进行编号,如下图所示: ![](http://qcdn.itcharge.cn/images/20210827143836.png) -假设当前单元格的起始位置为 `index`,则合并策略如下: +假设当前单元格的起始位置为 $index$,则合并策略如下: - 如果是单元格内: - - 如果是空格:合并 `index`、`index + 1`、`index + 2`、`index + 3`。 - - 如果是 `/`:合并 `index` 和 `index + 1`,合并 `index + 2` 和 `index + 3`。 - - 如果是 `\\`:合并 `index` 和 `index + 3`,合并 `index + 1` 和 `index + 2`。 + - 如果是空格:合并 $index$、$index + 1$、$index + 2$、$index + 3$。 + - 如果是 `'/'`:合并 $index$ 和 $index + 1$,合并 $index + 2$ 和 $index + 3$。 + - 如果是 `'\'`:合并 $index$ 和 $index + 3$,合并 $index + 1$ 和 $index + 2$。 - 如果是单元格间,则向下向右进行合并: - - 向下:合并 `index + 3` 和 `index + 4 * size + 1 `。 - - 向右:合并 `index + 2` 和 `index + 4`。 + - 向下:合并 $index + 3$ 和 $index + 4 * size + 1 $。 + - 向右:合并 $index + 2$ 和 $index + 4$。 最后合并完成之后,统计并查集中连通分量个数即为答案。 -## 代码 +### 思路 1:代码 ```python class UnionFind: @@ -91,3 +121,8 @@ class Solution: return union_find.count ``` +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(n^2 \times \alpha(n^2))$,其中 $\alpha$ 是反 `Ackerman` 函数。 +- **空间复杂度**:$O(n^2)$。 + diff --git "a/Solutions/1319. \350\277\236\351\200\232\347\275\221\347\273\234\347\232\204\346\223\215\344\275\234\346\254\241\346\225\260.md" "b/Solutions/1319. \350\277\236\351\200\232\347\275\221\347\273\234\347\232\204\346\223\215\344\275\234\346\254\241\346\225\260.md" index 6ab07b66..78d2a0be 100644 --- "a/Solutions/1319. \350\277\236\351\200\232\347\275\221\347\273\234\347\232\204\346\223\215\344\275\234\346\254\241\346\225\260.md" +++ "b/Solutions/1319. \350\277\236\351\200\232\347\275\221\347\273\234\347\232\204\346\223\215\344\275\234\346\254\241\346\225\260.md" @@ -5,24 +5,59 @@ ## 题目大意 -`n` 台计算机通过网线连接成一个网络,计算机的编号从 `0` 到 `n - 1`。线缆用 `comnnections` 表示,其中 `connections[i] = [a, b]` 表示连接了计算机 `a` 和 `b`。 +**描述**:$n$ 台计算机通过网线连接成一个网络,计算机的编号从 $0$ 到 $n - 1$。线缆用 $comnnections$ 表示,其中 $connections[i] = [a, b]$ 表示连接了计算机 $a$ 和 $b$。 -给定这个计算机网络的初始布线 `connections`,可以拔除任意两台直接相连的计算机之间的网线,并用这根网线连接任意一对未直接连接的计算机。现在要求:计算并返回使所有计算机都连通所需的最少操作次数。如果不可能,则返回 `-1`。 +给定这个计算机网络的初始布线 $connections$,可以拔除任意两台直接相连的计算机之间的网线,并用这根网线连接任意一对未直接连接的计算机。 + +**要求**:计算并返回使所有计算机都连通所需的最少操作次数。如果不可能,则返回 $-1$。 + +**说明**: + +- $1 \le n \le 10^5$。 +- $1 \le connections.length \le min( \frac{n \times (n-1)}{2}, 10^5)$。 +- $connections[i].length == 2$。 +- $0 \le connections[i][0], connections[i][1] < n$。 +- $connections[i][0] != connections[i][1]$。 +- 没有重复的连接。 +- 两台计算机不会通过多条线缆连接。 + +**示例**: + +- 示例 1: + +![](https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/01/11/sample_1_1677.png) + +```python +输入:n = 4, connections = [[0,1],[0,2],[1,2]] +输出:1 +解释:拔下计算机 1 和 2 之间的线缆,并将它插到计算机 1 和 3 上。 +``` + +- 示例 2: + +![](https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2020/01/11/sample_2_1677.png) + +```python +输入:n = 6, connections = [[0,1],[0,2],[0,3],[1,2],[1,3]] +输出:2 +``` ## 解题思路 -`n` 台计算机至少需要 `n - 1` 根线才能进行连接,如果网线的数量少于 `n - 1`,那么就不可能将其连接。接下来计算最少操作次数。 +### 思路 1:并查集 + +$n$ 台计算机至少需要 $n - 1$ 根线才能进行连接,如果网线的数量少于 $n - 1$,那么就不可能将其连接。接下来计算最少操作次数。 -把 `n` 台计算机看做是 `n` 个节点,每条网线看做是一条无向边。维护两个变量:多余电线数 `removeCount`、需要电线数 `needConnectCount`。初始 `removeCount = 1, needConnectCount = n - 1`。 +把 $n$ 台计算机看做是 $n$ 个节点,每条网线看做是一条无向边。维护两个变量:多余电线数 $removeCount$、需要电线数 $needConnectCount$。初始 $removeCount = 1, needConnectCount = n - 1$。 -遍历网线数组,将相连的节点 `a` 和 `b` 利用并查集加入到一个集合中(调用 `union` 操作)。 +遍历网线数组,将相连的节点 $a$ 和 $b$ 利用并查集加入到一个集合中(调用 `union` 操作)。 -- 如果 `a` 和 `b` 已经在同一个集合中,说明该连接线多余,多余电线数 +1。 -- 如果 `a` 和 `b` 不在一个集合中,则将其合并,则 `a` 和 `b` 之间不再需要用额外的电线连接了,所以需要电线数 -1。 +- 如果 $a$ 和 $b$ 已经在同一个集合中,说明该连接线多余,多余电线数加 $1$。 +- 如果 $a$ 和 $b$ 不在一个集合中,则将其合并,则 $a$ 和 $b$ 之间不再需要用额外的电线连接了,所以需要电线数减 $1$。 -最后,判断多余的电线数是否满足需要电线数,不满足返回 -1,如果满足,则返回需要电线数。 +最后,判断多余的电线数是否满足需要电线数,不满足返回 $-1$,如果满足,则返回需要电线数。 -## 代码 +### 思路 1:代码 ```python class UnionFind: @@ -63,3 +98,8 @@ class Solution: return needConnectCount ``` +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(m \times \alpha(n))$,其中 $m$ 是数组 $connections$ 的长度,$\alpha$ 是反 `Ackerman` 函数。 +- **空间复杂度**:$O(n)$。 +