diff --git a/problems/0001.两数之和.md b/problems/0001.两数之和.md index bf1e173e..4e44d0c3 100644 --- a/problems/0001.两数之和.md +++ b/problems/0001.两数之和.md @@ -37,20 +37,20 @@ [242. 有效的字母异位词](https://www.programmercarl.com/0242.有效的字母异位词.html) 这道题目是用数组作为哈希表来解决哈希问题,[349. 两个数组的交集](https://www.programmercarl.com/0349.两个数组的交集.html)这道题目是通过set作为哈希表来解决哈希问题。 -首先我在强调一下 **什么时候使用哈希法**,当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。 +首先我再强调一下 **什么时候使用哈希法**,当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。 本题呢,我就需要一个集合来存放我们遍历过的元素,然后在遍历数组的时候去询问这个集合,某元素是否遍历过,也就是 是否出现在这个集合。 那么我们就应该想到使用哈希法了。 -因为本地,我们不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,**需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适**。 +因为本题,我们不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,**需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适**。 再来看一下使用数组和set来做哈希法的局限。 * 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。 * set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。 -此时就要选择另一种数据结构:map ,map是一种key value的存储结构,可以用key保存数值,用value在保存数值所在的下标。 +此时就要选择另一种数据结构:map ,map是一种key value的存储结构,可以用key保存数值,用value再保存数值所在的下标。 C++中map,有三种类型: @@ -318,7 +318,7 @@ function twoSum(nums: number[], target: number): number[] { }; ``` -### php: +### PhP: ```php function twoSum(array $nums, int $target): array @@ -501,3 +501,4 @@ int* twoSum(int* nums, int numsSize, int target, int* returnSize){ + diff --git a/problems/0027.移除元素.md b/problems/0027.移除元素.md index ce9eccf0..40ee3a2e 100644 --- a/problems/0027.移除元素.md +++ b/problems/0027.移除元素.md @@ -152,10 +152,10 @@ public: ## 相关题目推荐 -* 26.删除排序数组中的重复项 -* 283.移动零 -* 844.比较含退格的字符串 -* 977.有序数组的平方 +* [26.删除排序数组中的重复项](https://leetcode.cn/problems/remove-duplicates-from-sorted-array/) +* [283.移动零](https://leetcode.cn/problems/move-zeroes/) +* [844.比较含退格的字符串](https://leetcode.cn/problems/backspace-string-compare/) +* [977.有序数组的平方](https://leetcode.cn/problems/squares-of-a-sorted-array/) ## 其他语言版本 @@ -444,3 +444,4 @@ public class Solution { + diff --git a/problems/0039.组合总和.md b/problems/0039.组合总和.md index 2a36ce5a..37d5614e 100644 --- a/problems/0039.组合总和.md +++ b/problems/0039.组合总和.md @@ -73,7 +73,7 @@ candidates 中的数字可以无限制重复被选取。 如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex,例如:[17.电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html) -**注意以上我只是说求组合的情况,如果是排列问题,又是另一套分析的套路,后面我再讲解排列的时候就重点介绍**。 +**注意以上我只是说求组合的情况,如果是排列问题,又是另一套分析的套路,后面我在讲解排列的时候会重点介绍**。 代码如下: @@ -311,7 +311,7 @@ class Solution: for i in range(startIndex, len(candidates)): if total + candidates[i] > target: - break + continue total += candidates[i] path.append(candidates[i]) self.backtracking(candidates, target, total, i, path, result) diff --git a/problems/0053.最大子序和.md b/problems/0053.最大子序和.md index fe4e4ed3..639c54bc 100644 --- a/problems/0053.最大子序和.md +++ b/problems/0053.最大子序和.md @@ -16,11 +16,13 @@ - 输出: 6 - 解释:  连续子数组  [4,-1,2,1] 的和最大,为  6。 -# 视频讲解 +## 算法公开课 -**《代码随想录》算法视频公开课:[贪心算法的巧妙需要慢慢体会!LeetCode:53. 最大子序和](https://www.bilibili.com/video/BV1aY4y1Z7ya),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 +**[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[贪心算法的巧妙需要慢慢体会!LeetCode:53. 最大子序和](https://www.bilibili.com/video/BV1aY4y1Z7ya),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 -## 暴力解法 +## 思路 + +### 暴力解法 暴力解法的思路,第一层 for 就是设置起始位置,第二层 for 循环遍历数组寻找最大值 @@ -48,7 +50,7 @@ public: 以上暴力的解法 C++勉强可以过,其他语言就不确定了。 -## 贪心解法 +### 贪心解法 **贪心贪的是哪里呢?** @@ -104,7 +106,7 @@ public: 当然题目没有说如果数组为空,应该返回什么,所以数组为空的话返回啥都可以了。 -## 常见误区 +### 常见误区 误区一: @@ -122,7 +124,7 @@ public: 其实并不会,因为还有一个变量 result 一直在更新 最大的连续和,只要有更大的连续和出现,result 就更新了,那么 result 已经把 4 更新了,后面 连续和变成 3,也不会对最后结果有影响。 -## 动态规划 +### 动态规划 当然本题还可以用动态规划来做,在代码随想录动态规划章节我会详细介绍,如果大家想在想看,可以直接跳转:[动态规划版本详解](https://programmercarl.com/0053.%E6%9C%80%E5%A4%A7%E5%AD%90%E5%BA%8F%E5%92%8C%EF%BC%88%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%EF%BC%89.html#%E6%80%9D%E8%B7%AF) diff --git a/problems/0054.螺旋矩阵.md b/problems/0054.螺旋矩阵.md index a38e8237..c62eb2b1 100644 --- a/problems/0054.螺旋矩阵.md +++ b/problems/0054.螺旋矩阵.md @@ -6,7 +6,7 @@ -## 54.螺旋矩阵 +# 54.螺旋矩阵 [力扣题目链接](https://leetcode.cn/problems/spiral-matrix/) @@ -200,6 +200,58 @@ class Solution { } ``` +### Javascript +``` +/** + * @param {number[][]} matrix + * @return {number[]} + */ +var spiralOrder = function(matrix) { + let m = matrix.length + let n = matrix[0].length + + let startX = startY = 0 + let i = 0 + let arr = new Array(m*n).fill(0) + let offset = 1 + let loop = mid = Math.floor(Math.min(m,n) / 2) + while (loop--) { + let row = startX + let col = startY + // --> + for (; col < n + startY - offset; col++) { + arr[i++] = matrix[row][col] + } + // down + for (; row < m + startX - offset; row++) { + arr[i++] = matrix[row][col] + } + // <-- + for (; col > startY; col--) { + arr[i++] = matrix[row][col] + } + for (; row > startX; row--) { + arr[i++] = matrix[row][col] + } + startX++ + startY++ + offset += 2 + } + if (Math.min(m, n) % 2 === 1) { + if (n > m) { + for (let j = mid; j < mid + n - m + 1; j++) { + arr[i++] = matrix[mid][j] + } + } else { + for (let j = mid; j < mid + m - n + 1; j++) { + arr[i++] = matrix[j][mid] + } + } + } + return arr +}; +``` +

diff --git a/problems/0059.螺旋矩阵II.md b/problems/0059.螺旋矩阵II.md index 73e9e4da..78d9385a 100644 --- a/problems/0059.螺旋矩阵II.md +++ b/problems/0059.螺旋矩阵II.md @@ -688,7 +688,7 @@ public class Solution { } ``` -### Ruby#: +### Ruby: ```ruby def generate_matrix(n) result = Array.new(n) { Array.new(n, 0) } diff --git a/problems/0062.不同路径.md b/problems/0062.不同路径.md index 5111e30e..985c7575 100644 --- a/problems/0062.不同路径.md +++ b/problems/0062.不同路径.md @@ -50,9 +50,9 @@ * 1 <= m, n <= 100 * 题目数据保证答案小于等于 2 * 10^9 -# 算法公开课 +## 算法公开课 -**《代码随想录》算法视频公开课:[动态规划中如何初始化很重要!| LeetCode:62.不同路径](https://www.bilibili.com/video/BV1ve4y1x7Eu/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 +**[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[动态规划中如何初始化很重要!| LeetCode:62.不同路径](https://www.bilibili.com/video/BV1ve4y1x7Eu/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 ## 思路 diff --git a/problems/0063.不同路径II.md b/problems/0063.不同路径II.md index cb305b41..3d243a7a 100644 --- a/problems/0063.不同路径II.md +++ b/problems/0063.不同路径II.md @@ -46,9 +46,9 @@ * 1 <= m, n <= 100 * obstacleGrid[i][j] 为 0 或 1 -# 算法公开课 +## 算法公开课 -**《代码随想录》算法视频公开课:[动态规划,这次遇到障碍了| LeetCode:63. 不同路径 II](https://www.bilibili.com/video/BV1Ld4y1k7c6/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 +**[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[动态规划,这次遇到障碍了| LeetCode:63. 不同路径 II](https://www.bilibili.com/video/BV1Ld4y1k7c6/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 ## 思路 diff --git a/problems/0072.编辑距离.md b/problems/0072.编辑距离.md index 1ed9a860..2b5a1925 100644 --- a/problems/0072.编辑距离.md +++ b/problems/0072.编辑距离.md @@ -403,6 +403,64 @@ int minDistance(char * word1, char * word2){ } ``` +Rust: + +```rust +impl Solution { + pub fn min_distance(word1: String, word2: String) -> i32 { + let mut dp = vec![vec![0; word2.len() + 1]; word1.len() + 1]; + for i in 1..=word2.len() { + dp[0][i] = i; + } + + for (j, v) in dp.iter_mut().enumerate().skip(1) { + v[0] = j; + } + for (i, char1) in word1.chars().enumerate() { + for (j, char2) in word2.chars().enumerate() { + if char1 == char2 { + dp[i + 1][j + 1] = dp[i][j]; + continue; + } + dp[i + 1][j + 1] = dp[i][j + 1].min(dp[i + 1][j]).min(dp[i][j]) + 1; + } + } + + dp[word1.len()][word2.len()] as i32 + } +} +``` + +> 一维 dp + +```rust +impl Solution { + pub fn min_distance(word1: String, word2: String) -> i32 { + let mut dp = vec![0; word1.len() + 1]; + for (i, v) in dp.iter_mut().enumerate().skip(1) { + *v = i; + } + + for char2 in word2.chars() { + // 相当于 dp[i][0] 的初始化 + let mut pre = dp[0]; + dp[0] += 1; // j = 0, 将前 i 个字符变成空串的个数 + for (j, char1) in word1.chars().enumerate() { + let temp = dp[j + 1]; + if char1 == char2 { + dp[j + 1] = pre; + } else { + dp[j + 1] = dp[j + 1].min(dp[j]).min(pre) + 1; + } + pre = temp; + } + } + + dp[word1.len()] as i32 + } +} +``` +

diff --git a/problems/0084.柱状图中最大的矩形.md b/problems/0084.柱状图中最大的矩形.md index 9b76229a..9fb6a6b0 100644 --- a/problems/0084.柱状图中最大的矩形.md +++ b/problems/0084.柱状图中最大的矩形.md @@ -725,62 +725,6 @@ impl Solution { } ``` -Rust - -双指针预处理 -```rust - -impl Solution { - pub fn largest_rectangle_area(v: Vec) -> i32 { - let n = v.len(); - let mut left_smaller_idx = vec![-1; n]; - let mut right_smaller_idx = vec![n as i32; n]; - for i in 1..n { - let mut mid = i as i32 - 1; - while mid >= 0 && v[mid as usize] >= v[i] { - mid = left_smaller_idx[mid as usize]; - } - left_smaller_idx[i] = mid; - } - for i in (0..n-1).rev() { - let mut mid = i + 1; - while mid < n && v[mid] >= v[i] { - mid = right_smaller_idx[mid] as usize; - } - right_smaller_idx[i] = mid as i32; - } - let mut res = 0; - for (idx, &e) in v.iter().enumerate() { - res = res.max((right_smaller_idx[idx] - left_smaller_idx[idx] - 1) * e); - } - dbg!(res) - } -} -``` - -单调栈 -```rust -impl Solution { - pub fn largest_rectangle_area1(mut v: Vec) -> i32 { - v.insert(0, 0); // 便于使第一个元素能够有左侧<=它的值 - v.push(0); // 便于在结束处理最后一个元素后清空残留在栈中的值 - let mut res = 0; - let mut stack = vec![]; // 递增的栈 - for (idx, &e) in v.iter().enumerate() { - while !stack.is_empty() && v[*stack.last().unwrap()] > e { - let pos = stack.pop().unwrap(); - let prev_pos = *stack.last().unwrap(); - let s = (idx - prev_pos - 1) as i32 * v[pos]; - res = res.max(s); - } - stack.push(idx); - } - res - } -} -``` - -

diff --git a/problems/0090.子集II.md b/problems/0090.子集II.md index 88c8bdad..13080cd9 100644 --- a/problems/0090.子集II.md +++ b/problems/0090.子集II.md @@ -3,6 +3,7 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

+ # 90.子集II [力扣题目链接](https://leetcode.cn/problems/subsets-ii/) diff --git a/problems/0096.不同的二叉搜索树.md b/problems/0096.不同的二叉搜索树.md index 368a5747..8d58cc5a 100644 --- a/problems/0096.不同的二叉搜索树.md +++ b/problems/0096.不同的二叉搜索树.md @@ -16,9 +16,9 @@ ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210113161941835.png) -# 算法公开课 +## 算法公开课 -**《代码随想录》算法视频公开课:[动态规划找到子状态之间的关系很重要!| LeetCode:96.不同的二叉搜索树](https://www.bilibili.com/video/BV1eK411o7QA/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 +**[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[动态规划找到子状态之间的关系很重要!| LeetCode:96.不同的二叉搜索树](https://www.bilibili.com/video/BV1eK411o7QA/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 ## 思路 diff --git a/problems/0115.不同的子序列.md b/problems/0115.不同的子序列.md index d925c5de..0d2477bd 100644 --- a/problems/0115.不同的子序列.md +++ b/problems/0115.不同的子序列.md @@ -314,6 +314,63 @@ function numDistinct(s: string, t: string): number { }; ``` +### Rust: + +```rust +impl Solution { + pub fn num_distinct(s: String, t: String) -> i32 { + if s.len() < t.len() { + return 0; + } + let mut dp = vec![vec![0; s.len() + 1]; t.len() + 1]; + // i = 0, t 为空字符串,s 作为子序列的个数为 1(删除 s 所有元素) + dp[0] = vec![1; s.len() + 1]; + for (i, char_t) in t.chars().enumerate() { + for (j, char_s) in s.chars().enumerate() { + if char_t == char_s { + // t 的前 i 个字符在 s 的前 j 个字符中作为子序列的个数 + dp[i + 1][j + 1] = dp[i][j] + dp[i + 1][j]; + continue; + } + dp[i + 1][j + 1] = dp[i + 1][j]; + } + } + dp[t.len()][s.len()] + } +} +``` + +> 滚动数组 + +```rust +impl Solution { + pub fn num_distinct(s: String, t: String) -> i32 { + if s.len() < t.len() { + return 0; + } + let (s, t) = (s.into_bytes(), t.into_bytes()); + // 对于 t 为空字符串,s 作为子序列的个数为 1(删除 s 所有元素) + let mut dp = vec![1; s.len() + 1]; + for char_t in t { + // dp[i - 1][j - 1],dp[j + 1] 更新之前的值 + let mut pre = dp[0]; + // 当开始遍历 t,s 的前 0 个字符无法包含任意子序列 + dp[0] = 0; + for (j, &char_s) in s.iter().enumerate() { + let temp = dp[j + 1]; + if char_t == char_s { + dp[j + 1] = pre + dp[j]; + } else { + dp[j + 1] = dp[j]; + } + pre = temp; + } + } + dp[s.len()] + } +} +``` +

diff --git a/problems/0151.翻转字符串里的单词.md b/problems/0151.翻转字符串里的单词.md index 19ccb725..0c1a526f 100644 --- a/problems/0151.翻转字符串里的单词.md +++ b/problems/0151.翻转字符串里的单词.md @@ -433,7 +433,7 @@ class Solution { } ``` -### python: +### Python: (版本一)先删除空白,然后整个反转,最后单词反转。 **因为字符串是不可变类型,所以反转单词的时候,需要将其转换成列表,然后通过join函数再将其转换成列表,所以空间复杂度不是O(1)** @@ -547,26 +547,28 @@ func reverseWords(s string) string { b = b[:slowIndex] } //2.反转整个字符串 - reverse(&b, 0, len(b)-1) + reverse(b) //3.反转单个单词 i单词开始位置,j单词结束位置 i := 0 for i < len(b) { j := i for ; j < len(b) && b[j] != ' '; j++ { } - reverse(&b, i, j-1) + reverse(b[i:j]) i = j i++ } return string(b) } -func reverse(b *[]byte, left, right int) { - for left < right { - (*b)[left], (*b)[right] = (*b)[right], (*b)[left] - left++ - right-- - } +func reverse(b []byte) { + left := 0 + right := len(b) - 1 + for left < right { + b[left], b[right] = b[right], b[left] + left++ + right-- + } } ``` @@ -974,4 +976,3 @@ char * reverseWords(char * s){ - diff --git a/problems/0188.买卖股票的最佳时机IV.md b/problems/0188.买卖股票的最佳时机IV.md index d4dc7698..e4c5c484 100644 --- a/problems/0188.买卖股票的最佳时机IV.md +++ b/problems/0188.买卖股票的最佳时机IV.md @@ -529,4 +529,3 @@ impl Solution { - diff --git a/problems/0200.岛屿数量.广搜版.md b/problems/0200.岛屿数量.广搜版.md index c20fe4f1..5b9d90aa 100644 --- a/problems/0200.岛屿数量.广搜版.md +++ b/problems/0200.岛屿数量.广搜版.md @@ -197,7 +197,7 @@ class Solution { } ``` -## 其他语言版本 + ### Python BFS solution ```python @@ -237,6 +237,7 @@ class Solution: continue q.append((next_i, next_j)) visited[next_i][next_j] = True + ```

@@ -244,3 +245,4 @@ class Solution: ``` + diff --git a/problems/0200.岛屿数量.深搜版.md b/problems/0200.岛屿数量.深搜版.md index c30ace19..f610e323 100644 --- a/problems/0200.岛屿数量.深搜版.md +++ b/problems/0200.岛屿数量.深搜版.md @@ -218,6 +218,67 @@ class Solution { } } ``` + +Python: + +```python +# 版本一 +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + m, n = len(grid), len(grid[0]) + visited = [[False] * n for _ in range(m)] + dirs = [(-1, 0), (0, 1), (1, 0), (0, -1)] # 四个方向 + result = 0 + + def dfs(x, y): + for d in dirs: + nextx = x + d[0] + nexty = y + d[1] + if nextx < 0 or nextx >= m or nexty < 0 or nexty >= n: # 越界了,直接跳过 + continue + if not visited[nextx][nexty] and grid[nextx][nexty] == '1': # 没有访问过的同时是陆地的 + visited[nextx][nexty] = True + dfs(nextx, nexty) + + for i in range(m): + for j in range(n): + if not visited[i][j] and grid[i][j] == '1': + visited[i][j] = True + result += 1 # 遇到没访问过的陆地,+1 + dfs(i, j) # 将与其链接的陆地都标记上 true + + return result +``` + +```python +# 版本二 +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + m, n = len(grid), len(grid[0]) + visited = [[False] * n for _ in range(m)] + dirs = [(-1, 0), (0, 1), (1, 0), (0, -1)] # 四个方向 + result = 0 + + def dfs(x, y): + if visited[x][y] or grid[x][y] == '0': + return # 终止条件:访问过的节点 或者 遇到海水 + visited[x][y] = True + for d in dirs: + nextx = x + d[0] + nexty = y + d[1] + if nextx < 0 or nextx >= m or nexty < 0 or nexty >= n: # 越界了,直接跳过 + continue + dfs(nextx, nexty) + + for i in range(m): + for j in range(n): + if not visited[i][j] and grid[i][j] == '1': + result += 1 # 遇到没访问过的陆地,+1 + dfs(i, j) # 将与其链接的陆地都标记上 true + + return result +``` +

diff --git a/problems/0202.快乐数.md b/problems/0202.快乐数.md index 4a77e2b6..719672a2 100644 --- a/problems/0202.快乐数.md +++ b/problems/0202.快乐数.md @@ -423,29 +423,61 @@ impl Solution { ### C: ```C -typedef struct HashNodeTag { - int key; /* num */ - struct HashNodeTag *next; -}HashNode; - -/* Calcualte the hash key */ -static inline int hash(int key, int size) { - int index = key % size; - return (index > 0) ? (index) : (-index); -} - -/* Calculate the sum of the squares of its digits*/ -static inline int calcSquareSum(int num) { - unsigned int sum = 0; - while(num > 0) { - sum += (num % 10) * (num % 10); - num = num/10; +int get_sum(int n) { + int sum = 0; + div_t n_div = { .quot = n }; + while (n_div.quot != 0) { + n_div = div(n_div.quot, 10); + sum += n_div.rem * n_div.rem; } return sum; } +// (版本1)使用数组 +bool isHappy(int n) { + // sum = a1^2 + a2^2 + ... ak^2 + // first round: + // 1 <= k <= 10 + // 1 <= sum <= 1 + 81 * 9 = 730 + // second round: + // 1 <= k <= 3 + // 1 <= sum <= 36 + 81 * 2 = 198 + // third round: + // 1 <= sum <= 81 * 2 = 162 + // fourth round: + // 1 <= sum <= 81 * 2 = 162 -Scala: + uint8_t visited[163] = { 0 }; + int sum = get_sum(get_sum(n)); + int next_n = sum; + + while (next_n != 1) { + sum = get_sum(next_n); + + if (visited[sum]) return false; + + visited[sum] = 1; + next_n = sum; + }; + + return true; +} + +// (版本2)使用快慢指针 +bool isHappy(int n) { + int slow = n; + int fast = n; + + do { + slow = get_sum(slow); + fast = get_sum(get_sum(fast)); + } while (slow != fast); + + return (fast == 1); +} +``` + +### Scala: ```scala object Solution { // 引入mutable diff --git a/problems/0283.移动零.md b/problems/0283.移动零.md index ee3f4291..42232cc0 100644 --- a/problems/0283.移动零.md +++ b/problems/0283.移动零.md @@ -3,6 +3,7 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

+ # 283. 移动零:动态规划:一样的套路,再求一次完全平方数 [力扣题目链接](https://leetcode.cn/problems/move-zeroes/) diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md index 11cf13d9..677c72c6 100644 --- a/problems/0300.最长上升子序列.md +++ b/problems/0300.最长上升子序列.md @@ -130,18 +130,16 @@ public: class Solution { public int lengthOfLIS(int[] nums) { int[] dp = new int[nums.length]; + int res = 0; Arrays.fill(dp, 1); - for (int i = 0; i < dp.length; i++) { + for (int i = 1; i < dp.length; i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) { dp[i] = Math.max(dp[i], dp[j] + 1); } + res = Math.max(res, dp[i]); } } - int res = 0; - for (int i = 0; i < dp.length; i++) { - res = Math.max(res, dp[i]); - } return res; } } @@ -294,7 +292,7 @@ function lengthOfLIS(nums: number[]): number { ```rust pub fn length_of_lis(nums: Vec) -> i32 { - let mut dp = vec![1; nums.len() + 1]; + let mut dp = vec![1; nums.len()]; let mut result = 1; for i in 1..nums.len() { for j in 0..i { @@ -309,7 +307,6 @@ pub fn length_of_lis(nums: Vec) -> i32 { ``` -

diff --git a/problems/0332.重新安排行程.md b/problems/0332.重新安排行程.md index 794a67fa..fcdeae7b 100644 --- a/problems/0332.重新安排行程.md +++ b/problems/0332.重新安排行程.md @@ -31,7 +31,7 @@ ## 算法公开课 -**如果对回溯算法基础还不了解的话,我还特意录制了一期视频,[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。 +**[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/) ,相信结合视频再看本篇题解,更有助于大家对本题的理解。** ## 思路 @@ -790,4 +790,3 @@ impl Solution { - diff --git a/problems/0343.整数拆分.md b/problems/0343.整数拆分.md index 3ff8dedb..cba82f6c 100644 --- a/problems/0343.整数拆分.md +++ b/problems/0343.整数拆分.md @@ -22,9 +22,9 @@ * 说明: 你可以假设 n 不小于 2 且不大于 58。 -# 算法公开课 +## 算法公开课 -**《代码随想录》算法视频公开课:[动态规划,本题关键在于理解递推公式!| LeetCode:343. 整数拆分](https://www.bilibili.com/video/BV1Mg411q7YJ/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 +**[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[动态规划,本题关键在于理解递推公式!| LeetCode:343. 整数拆分](https://www.bilibili.com/video/BV1Mg411q7YJ/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 ## 思路 @@ -473,3 +473,4 @@ object Solution { + diff --git a/problems/0349.两个数组的交集.md b/problems/0349.两个数组的交集.md index 8daf5a35..26a9286d 100644 --- a/problems/0349.两个数组的交集.md +++ b/problems/0349.两个数组的交集.md @@ -465,6 +465,25 @@ object Solution { } ``` + +###Ruby +```ruby +def intersection(nums1, nums2) + hash = {} + result = {} + + nums1.each do |num| + hash[num] = 1 if hash[num].nil? + end + + nums2.each do |num| + #取nums1和nums2交集 + result[num] = 1 if hash[num] != nil + end + + return result.keys +end +``` ## 相关题目 * [350.两个数组的交集 II](https://leetcode.cn/problems/intersection-of-two-arrays-ii/) diff --git a/problems/0383.赎金信.md b/problems/0383.赎金信.md index c15d1ac1..3de48ce3 100644 --- a/problems/0383.赎金信.md +++ b/problems/0383.赎金信.md @@ -68,7 +68,7 @@ public: ### 哈希解法 -因为题目所只有小写字母,那可以采用空间换取时间的哈希策略, 用一个长度为26的数组还记录magazine里字母出现的次数。 +因为题目说只有小写字母,那可以采用空间换取时间的哈希策略,用一个长度为26的数组来记录magazine里字母出现的次数。 然后再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母。 diff --git a/problems/0392.判断子序列.md b/problems/0392.判断子序列.md index 6342a41f..12d3fa48 100644 --- a/problems/0392.判断子序列.md +++ b/problems/0392.判断子序列.md @@ -259,9 +259,49 @@ func isSubsequence(s string, t string) bool { } ``` +Rust: +```rust +impl Solution { + pub fn is_subsequence(s: String, t: String) -> bool { + let mut dp = vec![vec![0; t.len() + 1]; s.len() + 1]; + for (i, char_s) in s.chars().enumerate() { + for (j, char_t) in t.chars().enumerate() { + if char_s == char_t { + dp[i + 1][j + 1] = dp[i][j] + 1; + continue; + } + dp[i + 1][j + 1] = dp[i + 1][j] + } + } + dp[s.len()][t.len()] == s.len() + } +} +``` +> 滚动数组 +```rust +impl Solution { + pub fn is_subsequence(s: String, t: String) -> bool { + let mut dp = vec![0; t.len() + 1]; + let (s, t) = (s.as_bytes(), t.as_bytes()); + for &byte_s in s { + let mut pre = 0; + for j in 0..t.len() { + let temp = dp[j + 1]; + if byte_s == t[j] { + dp[j + 1] = pre + 1; + } else { + dp[j + 1] = dp[j]; + } + pre = temp; + } + } + dp[t.len()] == s.len() + } +} +```

diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md index 0657c010..2b2be103 100644 --- a/problems/0416.分割等和子集.md +++ b/problems/0416.分割等和子集.md @@ -4,7 +4,7 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-## 416. 分割等和子集 +# 416. 分割等和子集 [力扣题目链接](https://leetcode.cn/problems/partition-equal-subset-sum/) @@ -730,4 +730,3 @@ object Solution { - diff --git a/problems/0463.岛屿的周长.md b/problems/0463.岛屿的周长.md index 14fa98dc..48e64d0b 100644 --- a/problems/0463.岛屿的周长.md +++ b/problems/0463.岛屿的周长.md @@ -302,6 +302,104 @@ var islandPerimeter = function(grid) { }; ``` +TypeScript: + +```typescript +/** + * 方法一:深度优先搜索(DFS) + * @param grid 二维网格地图,其中 grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域 + * @returns 岛屿的周长 + */ +function islandPerimeter(grid: number[][]): number { + // 处理特殊情况:网格为空或行列数为 0,直接返回 0 + if (!grid || grid.length === 0 || grid[0].length === 0) { + return 0; + } + + // 获取网格的行数和列数 + const rows = grid.length; + const cols = grid[0].length; + let perimeter = 0; // 岛屿的周长 + + /** + * 深度优先搜索函数 + * @param i 当前格子的行索引 + * @param j 当前格子的列索引 + */ + const dfs = (i: number, j: number) => { + // 如果当前位置超出网格范围,或者当前位置是水域(grid[i][j] === 0),则周长增加1 + if (i < 0 || i >= rows || j < 0 || j >= cols || grid[i][j] === 0) { + perimeter++; + return; + } + + // 如果当前位置已经访问过(grid[i][j] === -1),则直接返回 + if (grid[i][j] === -1) { + return; + } + + // 标记当前位置为已访问(-1),避免重复计算 + grid[i][j] = -1; + + // 继续搜索上、下、左、右四个方向 + dfs(i + 1, j); + dfs(i - 1, j); + dfs(i, j + 1); + dfs(i, j - 1); + }; + + // 遍历整个网格,找到第一个陆地格子(grid[i][j] === 1),并以此为起点进行深度优先搜索 + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (grid[i][j] === 1) { + dfs(i, j); + break; + } + } + } + + return perimeter; +} + +/** + * 方法二:遍历每个陆地格子,统计周长 + * @param grid 二维网格地图,其中 grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域 + * @returns 岛屿的周长 + */ +function islandPerimeter(grid: number[][]): number { + // 处理特殊情况:网格为空或行列数为 0,直接返回 0 + if (!grid || grid.length === 0 || grid[0].length === 0) { + return 0; + } + + // 获取网格的行数和列数 + const rows = grid.length; + const cols = grid[0].length; + let perimeter = 0; // 岛屿的周长 + + // 遍历整个网格 + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + // 如果当前格子是陆地(grid[i][j] === 1) + if (grid[i][j] === 1) { + perimeter += 4; // 周长先加上4个边 + + // 判断当前格子的上方是否也是陆地,如果是,则周长减去2个边 + if (i > 0 && grid[i - 1][j] === 1) { + perimeter -= 2; + } + + // 判断当前格子的左方是否也是陆地,如果是,则周长减去2个边 + if (j > 0 && grid[i][j - 1] === 1) { + perimeter -= 2; + } + } + } + } + + return perimeter; +} +```

diff --git a/problems/0503.下一个更大元素II.md b/problems/0503.下一个更大元素II.md index 023e4d7e..d211a680 100644 --- a/problems/0503.下一个更大元素II.md +++ b/problems/0503.下一个更大元素II.md @@ -294,4 +294,3 @@ impl Solution { - diff --git a/problems/0516.最长回文子序列.md b/problems/0516.最长回文子序列.md index 80927583..62f4a3da 100644 --- a/problems/0516.最长回文子序列.md +++ b/problems/0516.最长回文子序列.md @@ -272,7 +272,26 @@ function longestPalindromeSubseq(s: string): number { }; ``` +Rust: +```rust +impl Solution { + pub fn longest_palindrome_subseq(s: String) -> i32 { + let mut dp = vec![vec![0; s.len()]; s.len()]; + for i in (0..s.len()).rev() { + dp[i][i] = 1; + for j in i + 1..s.len() { + if s[i..=i] == s[j..=j] { + dp[i][j] = dp[i + 1][j - 1] + 2; + continue; + } + dp[i][j] = dp[i + 1][j].max(dp[i][j - 1]); + } + } + dp[0][s.len() - 1] + } +} +```

diff --git a/problems/0583.两个字符串的删除操作.md b/problems/0583.两个字符串的删除操作.md index 505a4e33..7dbb8ef5 100644 --- a/problems/0583.两个字符串的删除操作.md +++ b/problems/0583.两个字符串的删除操作.md @@ -370,7 +370,51 @@ function minDistance(word1: string, word2: string): number { }; ``` +Rust: +```rust +impl Solution { + pub fn min_distance(word1: String, word2: String) -> i32 { + let mut dp = vec![vec![0; word2.len() + 1]; word1.len() + 1]; + for i in 0..word1.len() { + dp[i + 1][0] = i + 1; + } + for j in 0..word2.len() { + dp[0][j + 1] = j + 1; + } + for (i, char1) in word1.chars().enumerate() { + for (j, char2) in word2.chars().enumerate() { + if char1 == char2 { + dp[i + 1][j + 1] = dp[i][j]; + continue; + } + dp[i + 1][j + 1] = dp[i][j + 1].min(dp[i + 1][j]) + 1; + } + } + dp[word1.len()][word2.len()] as i32 + } +} +``` + +> 版本 2 + +```rust +impl Solution { + pub fn min_distance(word1: String, word2: String) -> i32 { + let mut dp = vec![vec![0; word2.len() + 1]; word1.len() + 1]; + for (i, char1) in word1.chars().enumerate() { + for (j, char2) in word2.chars().enumerate() { + if char1 == char2 { + dp[i + 1][j + 1] = dp[i][j] + 1; + continue; + } + dp[i + 1][j + 1] = dp[i][j + 1].max(dp[i + 1][j]); + } + } + (word1.len() + word2.len() - 2 * dp[word1.len()][word2.len()]) as i32 + } +} +```

diff --git a/problems/0617.合并二叉树.md b/problems/0617.合并二叉树.md index 44092aae..18839a26 100644 --- a/problems/0617.合并二叉树.md +++ b/problems/0617.合并二叉树.md @@ -19,7 +19,7 @@ 注意: 合并必须从两个树的根节点开始。 -# 算法公开课 +## 算法公开课 **[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[一起操作两个二叉树?有点懵!| LeetCode:617.合并二叉树](https://www.bilibili.com/video/BV1m14y1Y7JK),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 @@ -793,3 +793,4 @@ impl Solution { + diff --git a/problems/0647.回文子串.md b/problems/0647.回文子串.md index fdf83736..dba7218f 100644 --- a/problems/0647.回文子串.md +++ b/problems/0647.回文子串.md @@ -521,8 +521,51 @@ function expandRange(s: string, left: number, right: number): number { } ``` +Rust: +```rust +impl Solution { + pub fn count_substrings(s: String) -> i32 { + let mut dp = vec![vec![false; s.len()]; s.len()]; + let mut res = 0; + for i in (0..s.len()).rev() { + for j in i..s.len() { + if s[i..=i] == s[j..=j] && (j - i <= 1 || dp[i + 1][j - 1]) { + dp[i][j] = true; + res += 1; + } + } + } + res + } +} +``` + +> 双指针 + +```rust +impl Solution { + pub fn count_substrings(s: String) -> i32 { + let mut res = 0; + for i in 0..s.len() { + res += Self::extend(&s, i, i, s.len()); + res += Self::extend(&s, i, i + 1, s.len()); + } + res + } + + fn extend(s: &str, mut i: usize, mut j: usize, len: usize) -> i32 { + let mut res = 0; + while i < len && j < len && s[i..=i] == s[j..=j] { + res += 1; + i = i.wrapping_sub(1); + j += 1; + } + res + } +} +```

diff --git a/problems/0674.最长连续递增序列.md b/problems/0674.最长连续递增序列.md index 0c5e64b3..485e321c 100644 --- a/problems/0674.最长连续递增序列.md +++ b/problems/0674.最长连续递增序列.md @@ -302,8 +302,9 @@ func findLengthOfLCIS(nums []int) int { } ``` -### Rust: +### Rust: +>动态规划 ```rust pub fn find_length_of_lcis(nums: Vec) -> i32 { if nums.is_empty() { @@ -321,6 +322,27 @@ pub fn find_length_of_lcis(nums: Vec) -> i32 { } ``` + +> 贪心 + +```rust +impl Solution { + pub fn find_length_of_lcis(nums: Vec) -> i32 { + let (mut res, mut count) = (1, 1); + for i in 1..nums.len() { + if nums[i] > nums[i - 1] { + count += 1; + res = res.max(count); + continue; + } + count = 1; + } + res + } +} +``` + + ### Javascript: > 动态规划: diff --git a/problems/0695.岛屿的最大面积.md b/problems/0695.岛屿的最大面积.md index 37a601bc..186f044c 100644 --- a/problems/0695.岛屿的最大面积.md +++ b/problems/0695.岛屿的最大面积.md @@ -22,7 +22,7 @@ * 输出:6 * 解释:答案不应该是 11 ,因为岛屿只能包含水平或垂直这四个方向上的 1 。 -# 思路 +## 思路 注意题目中每座岛屿只能由**水平方向和/或竖直方向上**相邻的陆地连接形成。 @@ -38,7 +38,7 @@ * [DFS理论基础](https://programmercarl.com/图论深搜理论基础.html) * [BFS理论基础](https://programmercarl.com/图论广搜理论基础.html) -## DFS +### DFS 很多同学,写dfs其实也是凭感觉来,有的时候dfs函数中写终止条件才能过,有的时候 dfs函数不写终止添加也能过! @@ -134,7 +134,7 @@ public: 以上两种写法的区别,我在题解: [DFS,BDF 你没注意的细节都给你列出来了!LeetCode:200. 岛屿数量](https://leetcode.cn/problems/number-of-islands/solution/by-carlsun-2-n72a/)做了详细介绍。 -## BFS +### BFS 关于广度优先搜索,如果大家还不了解的话,看这里:[广度优先搜索精讲](https://programmercarl.com/图论广搜理论基础.html) @@ -188,9 +188,9 @@ public: ``` -# 其它语言版本 -## Java -### DFS +## 其它语言版本 +### Java +#### DFS ```java // DFS class Solution { @@ -236,7 +236,7 @@ class Solution { ``` -### BFS +#### BFS ```java //BFS class Solution { @@ -290,7 +290,7 @@ class Solution { } } ``` -### DFS 優化(遇到島嶼後,就把他淹沒) +#### DFS 優化(遇到島嶼後,就把他淹沒) ```java //这里使用深度优先搜索 DFS 来完成本道题目。我们使用 DFS 计算一个岛屿的面积,同时维护计算过的最大的岛屿面积。同时,为了避免对岛屿重复计算,我们在 DFS 的时候对岛屿进行 “淹没” 操作,即将岛屿所占的地方置为 0。 public int maxAreaOfIsland(int[][] grid) { @@ -319,8 +319,8 @@ public int dfs(int[][] grid,int i,int j){ } ``` -## Python -### BFS +### Python +#### BFS ```python class Solution: def __init__(self): @@ -359,7 +359,7 @@ class Solution: self.count += 1 queue.append((new_x, new_y)) ``` -### DFS +#### DFS ```python class Solution: def __init__(self): @@ -394,3 +394,4 @@ class Solution: + diff --git a/problems/0704.二分查找.md b/problems/0704.二分查找.md index 52abf578..ba5e538c 100644 --- a/problems/0704.二分查找.md +++ b/problems/0704.二分查找.md @@ -150,8 +150,8 @@ public: * [35.搜索插入位置](https://programmercarl.com/0035.搜索插入位置.html) * [34.在排序数组中查找元素的第一个和最后一个位置](https://programmercarl.com/0034.%E5%9C%A8%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E6%9F%A5%E6%89%BE%E5%85%83%E7%B4%A0%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E5%92%8C%E6%9C%80%E5%90%8E%E4%B8%80%E4%B8%AA%E4%BD%8D%E7%BD%AE.html) -* 69.x 的平方根 -* 367.有效的完全平方数 +* [69.x 的平方根](https://leetcode.cn/problems/sqrtx/) +* [367.有效的完全平方数](https://leetcode.cn/problems/valid-perfect-square/) @@ -760,9 +760,58 @@ object Solution { } } ``` +**Dart:** + +```dart +(版本一)左闭右闭区间 +class Solution { + int search(List nums, int target) { + int left = 0; + int right = nums.length - 1; + while (left <= right) { + int middle = ((left + right)/2).truncate(); + switch (nums[middle].compareTo(target)) { + case 1: + right = middle - 1; + continue; + case -1: + left = middle + 1; + continue; + default: + return middle; + } + } + return -1; + } +} + +(版本二)左闭右开区间 +class Solution { + int search(List nums, int target) { + int left = 0; + int right = nums.length; + while (left < right) { + int middle = left + ((right - left) >> 1); + switch (nums[middle].compareTo(target)) { + case 1: + right = middle; + continue; + case -1: + left = middle + 1; + continue; + default: + return middle; + } + } + return -1; + } +} +``` +

+ diff --git a/problems/0714.买卖股票的最佳时机含手续费.md b/problems/0714.买卖股票的最佳时机含手续费.md index 52b2be3b..db398649 100644 --- a/problems/0714.买卖股票的最佳时机含手续费.md +++ b/problems/0714.买卖股票的最佳时机含手续费.md @@ -39,7 +39,7 @@ 本题相对于[贪心算法:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html),多添加了一个条件就是手续费。 -## 贪心算法 +### 贪心算法 在[贪心算法:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html)中使用贪心策略不用关心具体什么时候买卖,只要收集每天的正利润,最后稳稳的就是最大利润了。 @@ -93,7 +93,7 @@ public: 大家也可以发现,情况三,那块代码是可以删掉的,我是为了让代码表达清晰,所以没有精简。 -## 动态规划 +### 动态规划 我在公众号「代码随想录」里将在下一个系列详细讲解动态规划,所以本题解先给出我的C++代码(带详细注释),感兴趣的同学可以自己先学习一下。 @@ -364,3 +364,4 @@ object Solution { + diff --git a/problems/0718.最长重复子数组.md b/problems/0718.最长重复子数组.md index 18cc0240..272cf2b2 100644 --- a/problems/0718.最长重复子数组.md +++ b/problems/0718.最长重复子数组.md @@ -536,6 +536,29 @@ function findLength(nums1: number[], nums2: number[]): number { }; ``` +Rust: + +> 滚动数组 + +```rust +impl Solution { + pub fn find_length(nums1: Vec, nums2: Vec) -> i32 { + let (mut res, mut dp) = (0, vec![0; nums2.len()]); + + for n1 in nums1 { + for j in (0..nums2.len()).rev() { + if n1 == nums2[j] { + dp[j] = if j == 0 { 1 } else { dp[j - 1] + 1 }; + res = res.max(dp[j]); + } else { + dp[j] = 0; + } + } + } + res + } +} +``` diff --git a/problems/0797.所有可能的路径.md b/problems/0797.所有可能的路径.md index 2ea4ae47..ec8288c6 100644 --- a/problems/0797.所有可能的路径.md +++ b/problems/0797.所有可能的路径.md @@ -149,7 +149,7 @@ public: ``` -# 总结 +## 总结 本题是比较基础的深度优先搜索模板题,这种有向图路径问题,最合适使用深搜,当然本题也可以使用广搜,但广搜相对来说就麻烦了一些,需要记录一下路径。 @@ -159,7 +159,7 @@ public: ## 其他语言版本 -Java +### Java ```Java // 深度优先遍历 @@ -190,7 +190,8 @@ class Solution { } ``` -Python +### Python + ```python class Solution: def __init__(self): @@ -216,9 +217,9 @@ class Solution: self.path.pop() # 回溯 ``` -### Go + +

- diff --git a/problems/0827.最大人工岛.md b/problems/0827.最大人工岛.md index 9112fabd..d7879825 100644 --- a/problems/0827.最大人工岛.md +++ b/problems/0827.最大人工岛.md @@ -29,7 +29,7 @@ * 输出: 4 * 解释: 没有0可以让我们变成1,面积依然为 4。 -# 思路 +## 思路 本题的一个暴力想法,应该是遍历地图尝试 将每一个 0 改成1,然后去搜索地图中的最大的岛屿面积。 @@ -219,9 +219,9 @@ public: }; ``` -# 其他语言版本 +## 其他语言版本 -## Java +### Java ```Java class Solution { @@ -286,4 +286,3 @@ class Solution { - diff --git a/problems/0844.比较含退格的字符串.md b/problems/0844.比较含退格的字符串.md index ac56f6f9..c7f52202 100644 --- a/problems/0844.比较含退格的字符串.md +++ b/problems/0844.比较含退格的字符串.md @@ -38,7 +38,7 @@ 本文将给出 空间复杂度O(n)的栈模拟方法 以及空间复杂度是O(1)的双指针方法。 -## 普通方法(使用栈的思路) +### 普通方法(使用栈的思路) 这道题目一看就是要使用栈的节奏,这种匹配(消除)问题也是栈的擅长所在,跟着一起刷题的同学应该知道,在[栈与队列:匹配问题都是栈的强项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html),我就已经提过了一次使用栈来做类似的事情了。 @@ -100,7 +100,7 @@ public: * 时间复杂度:O(n + m) * 空间复杂度:O(n + m) -## 优化方法(从后向前双指针) +### 优化方法(从后向前双指针) 当然还可以有使用 O(1) 的空间复杂度来解决该问题。 @@ -289,7 +289,7 @@ class Solution { } ``` -### python +### Python ```python class Solution: @@ -591,3 +591,4 @@ impl Solution { + diff --git a/problems/1002.查找常用字符.md b/problems/1002.查找常用字符.md index 1138d7fc..57fa7dae 100644 --- a/problems/1002.查找常用字符.md +++ b/problems/1002.查找常用字符.md @@ -525,6 +525,54 @@ impl Solution { } ``` +Ruby: +```ruby +def common_chars(words) + result = [] + #统计所有字符串里字符出现的最小频率 + hash = {} + #初始化标识 + is_first = true + + words.each do |word| + #记录共同字符 + chars = [] + word.split('').each do |chr| + #第一个字符串初始化 + if is_first + chars << chr + else + #字母之前出现过的最小次数 + if hash[chr] != nil && hash[chr] > 0 + hash[chr] -= 1 + chars << chr + end + end + end + + is_first = false + #清除hash,更新字符最小频率 + hash.clear + chars.each do |chr| + if hash[chr] != nil + hash[chr] += 1 + else + hash[chr] = 1 + end + end + end + + #字符最小频率hash转换为字符数组 + hash.keys.each do |key| + for i in 0..hash[key] - 1 + result << key + end + end + + return result +end +``` +

diff --git a/problems/1035.不相交的线.md b/problems/1035.不相交的线.md index 74e94c84..ff9e5dc4 100644 --- a/problems/1035.不相交的线.md +++ b/problems/1035.不相交的线.md @@ -155,21 +155,44 @@ func max(a, b int) int { ### Rust: ```rust -pub fn max_uncrossed_lines(nums1: Vec, nums2: Vec) -> i32 { - let (n, m) = (nums1.len(), nums2.len()); - let mut last = vec![0; m + 1]; // 记录滚动数组 - let mut dp = vec![0; m + 1]; - for i in 1..=n { - dp.swap_with_slice(&mut last); - for j in 1..=m { - if nums1[i - 1] == nums2[j - 1] { - dp[j] = last[j - 1] + 1; - } else { - dp[j] = last[j].max(dp[j - 1]); +impl Solution { + pub fn max_uncrossed_lines(nums1: Vec, nums2: Vec) -> i32 { + let mut dp = vec![vec![0; nums2.len() + 1]; nums1.len() + 1]; + for (i, num1) in nums1.iter().enumerate() { + for (j, num2) in nums2.iter().enumerate() { + if num1 == num2 { + dp[i + 1][j + 1] = dp[i][j] + 1; + } else { + dp[i + 1][j + 1] = dp[i][j + 1].max(dp[i + 1][j]); + } } } + dp[nums1.len()][nums2.len()] + } +} +``` + +> 滚动数组 + +```rust +impl Solution { + pub fn max_uncrossed_lines(nums1: Vec, nums2: Vec) -> i32 { + let mut dp = vec![0; nums2.len() + 1]; + for num1 in nums1 { + let mut prev = 0; + for (j, &num2) in nums2.iter().enumerate() { + let temp = dp[j + 1]; + if num1 == num2 { + // 使用上一次的状态,防止重复计算 + dp[j + 1] = prev + 1; + } else { + dp[j + 1] = dp[j + 1].max(dp[j]); + } + prev = temp; + } + } + dp[nums2.len()] } - dp[m] } ``` diff --git a/problems/1143.最长公共子序列.md b/problems/1143.最长公共子序列.md index 260b085e..f33391c3 100644 --- a/problems/1143.最长公共子序列.md +++ b/problems/1143.最长公共子序列.md @@ -329,25 +329,50 @@ function longestCommonSubsequence(text1: string, text2: string): number { }; ``` -### Rust: +### Rust ```rust -pub fn longest_common_subsequence(text1: String, text2: String) -> i32 { - let (n, m) = (text1.len(), text2.len()); - let (s1, s2) = (text1.as_bytes(), text2.as_bytes()); - let mut dp = vec![0; m + 1]; - let mut last = vec![0; m + 1]; - for i in 1..=n { - dp.swap_with_slice(&mut last); - for j in 1..=m { - dp[j] = if s1[i - 1] == s2[j - 1] { - last[j - 1] + 1 - } else { - last[j].max(dp[j - 1]) - }; +impl Solution { + pub fn longest_common_subsequence(text1: String, text2: String) -> i32 { + let mut dp = vec![vec![0; text2.len() + 1]; text1.len() + 1]; + for (i, c1) in text1.chars().enumerate() { + for (j, c2) in text2.chars().enumerate() { + if c1 == c2 { + dp[i + 1][j + 1] = dp[i][j] + 1; + } else { + dp[i + 1][j + 1] = dp[i][j + 1].max(dp[i + 1][j]); + } + } } + dp[text1.len()][text2.len()] + } +} +``` + +一维: + +```rust +impl Solution { + pub fn longest_common_subsequence(text1: String, text2: String) -> i32 { + let mut dp = vec![0; text2.len() + 1]; + for c1 in text1.chars() { + // 初始化 prev + let mut prev = 0; + + for (j, c2) in text2.chars().enumerate() { + let temp = dp[j + 1]; + if c1 == c2 { + // 使用上一次的状态,防止重复计算 + dp[j + 1] = prev + 1; + } else { + dp[j + 1] = dp[j + 1].max(dp[j]); + } + // 使用上一次的状态更新 prev + prev = temp; + } + } + dp[text2.len()] } - dp[m] } ``` diff --git a/problems/1207.独一无二的出现次数.md b/problems/1207.独一无二的出现次数.md index 83ebbcb7..2ccd30c3 100644 --- a/problems/1207.独一无二的出现次数.md +++ b/problems/1207.独一无二的出现次数.md @@ -200,10 +200,30 @@ function uniqueOccurrences(arr: number[]): boolean { }) return countMap.size === new Set(countMap.values()).size; }; +``` + + +### Go: +```Go +func uniqueOccurrences(arr []int) bool { + count := make(map[int]int) // 统计数字出现的频率 + for _, v := range arr { + count[v] += 1 + } + fre := make(map[int]struct{}) // 看相同频率是否重复出现 + for _, v := range count { + if _, ok := fre[v]; ok { + return false + } + fre[v] = struct{}{} + } + return true +} ``` +

diff --git a/problems/1791.找出星型图的中心节点.md b/problems/1791.找出星型图的中心节点.md index d44c1476..9bcc7ef9 100644 --- a/problems/1791.找出星型图的中心节点.md +++ b/problems/1791.找出星型图的中心节点.md @@ -3,6 +3,7 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

+ # 1791.找出星型图的中心节点 [题目链接](https://leetcode.cn/problems/find-center-of-star-graph/) @@ -55,7 +56,7 @@ public: return -1; } }; -``` +``` 以上代码中没有使用 unordered_map,因为遍历的时候,开辟新空间会浪费时间,而采用 vector,这是 空间换时间的一种策略。 diff --git a/problems/1971.寻找图中是否存在路径.md b/problems/1971.寻找图中是否存在路径.md index 5f1d8943..29e50ab8 100644 --- a/problems/1971.寻找图中是否存在路径.md +++ b/problems/1971.寻找图中是否存在路径.md @@ -138,3 +138,4 @@ public: + diff --git a/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md b/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md index 50287ce2..1982d449 100644 --- a/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md +++ b/problems/为了绝杀编辑距离,卡尔做了三步铺垫.md @@ -200,4 +200,3 @@ class Solution { - diff --git a/problems/剑指Offer58-II.左旋转字符串.md b/problems/剑指Offer58-II.左旋转字符串.md index 008b7915..a3fb7ab3 100644 --- a/problems/剑指Offer58-II.左旋转字符串.md +++ b/problems/剑指Offer58-II.左旋转字符串.md @@ -142,7 +142,7 @@ class Solution { } ``` -### python: +### Python: (版本一)使用切片 ```python @@ -338,7 +338,7 @@ func reverseString(_ s: inout [Character], startIndex: Int, endIndex: Int) { ``` -### PHP +### PHP: ```php function reverseLeftWords($s, $n) { @@ -418,4 +418,3 @@ impl Solution { - diff --git a/problems/动态规划总结篇.md b/problems/动态规划总结篇.md index c86376d3..e28bfd04 100644 --- a/problems/动态规划总结篇.md +++ b/problems/动态规划总结篇.md @@ -40,7 +40,7 @@ 好啦,我们再一起回顾一下,动态规划专题中我们都讲了哪些内容。 -## 动划基础 +## 动态规划基础 * [关于动态规划,你该了解这些!](https://programmercarl.com/动态规划理论基础.html) * [动态规划:斐波那契数](https://programmercarl.com/0509.斐波那契数.html) @@ -136,4 +136,3 @@ - diff --git a/problems/周总结/20201030回溯周末总结.md b/problems/周总结/20201030回溯周末总结.md index 61270161..7f6fd114 100644 --- a/problems/周总结/20201030回溯周末总结.md +++ b/problems/周总结/20201030回溯周末总结.md @@ -9,6 +9,8 @@ -------------------------- +# 本周小结!(回溯算法系列一) + ## 周一 本周我们正式开始了回溯算法系列,那么首先当然是概述。 diff --git a/problems/周总结/20201107回溯周末总结.md b/problems/周总结/20201107回溯周末总结.md index 76bd331b..3f1d1012 100644 --- a/problems/周总结/20201107回溯周末总结.md +++ b/problems/周总结/20201107回溯周末总结.md @@ -17,7 +17,7 @@ 如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex,例如:[回溯算法:电话号码的字母组合](https://programmercarl.com/0017.电话号码的字母组合.html) -**注意以上我只是说求组合的情况,如果是排列问题,又是另一套分析的套路,后面我再讲解排列的时候就重点介绍**。 +**注意以上我只是说求组合的情况,如果是排列问题,又是另一套分析的套路,后面我在讲解排列的时候会重点介绍**。 最后还给出了本题的剪枝优化,如下: diff --git a/problems/哈希表总结.md b/problems/哈希表总结.md index 67506363..465ef9d1 100644 --- a/problems/哈希表总结.md +++ b/problems/哈希表总结.md @@ -16,7 +16,7 @@ **一般来说哈希表都是用来快速判断一个元素是否出现集合里**。 -对于哈希表,要知道**哈希函数**和**哈希碰撞**在哈希表中的作用. +对于哈希表,要知道**哈希函数**和**哈希碰撞**在哈希表中的作用。 哈希函数是把传入的key映射到符号表的索引上。 @@ -88,7 +88,7 @@ std::set和std::multiset底层实现都是红黑树,std::unordered_set的底 map是一种``的结构,本题可以用key保存数值,用value在保存数值所在的下标。所以使用map最为合适。 -C++提供如下三种map::(详情请看[关于哈希表,你该了解这些!](https://programmercarl.com/哈希表理论基础.html)) +C++提供如下三种map:(详情请看[关于哈希表,你该了解这些!](https://programmercarl.com/哈希表理论基础.html)) * std::map * std::multimap diff --git a/problems/栈与队列总结.md b/problems/栈与队列总结.md index 31ce9554..e7f8ef86 100644 --- a/problems/栈与队列总结.md +++ b/problems/栈与队列总结.md @@ -3,6 +3,7 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

+ # 栈与队列总结篇 ## 栈与队列的理论基础 diff --git a/problems/根据身高重建队列(vector原理讲解).md b/problems/根据身高重建队列(vector原理讲解).md index fdb4f58b..4291c80c 100644 --- a/problems/根据身高重建队列(vector原理讲解).md +++ b/problems/根据身高重建队列(vector原理讲解).md @@ -196,7 +196,7 @@ impl Solution{ } ``` -### Go: +### Go Go中slice的`append`操作和C++中vector的扩容机制基本相同。 @@ -213,4 +213,3 @@ Go中slice的`append`操作和C++中vector的扩容机制基本相同。 - diff --git a/problems/算法模板.md b/problems/算法模板.md index 59de69df..21b7f93b 100644 --- a/problems/算法模板.md +++ b/problems/算法模板.md @@ -3,8 +3,11 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

+# 算法模板 -## 二分查找法 +## 算法模板 + +### 二分查找法 ```CPP class Solution { @@ -29,7 +32,7 @@ public: ``` -## KMP +### KMP ```CPP void kmp(int* next, const string& s){ @@ -47,7 +50,7 @@ void kmp(int* next, const string& s){ } ``` -## 二叉树 +### 二叉树 二叉树的定义: @@ -60,7 +63,7 @@ struct TreeNode { }; ``` -### 深度优先遍历(递归) +#### 深度优先遍历(递归) 前序遍历(中左右) ```CPP @@ -90,7 +93,7 @@ void traversal(TreeNode* cur, vector& vec) { } ``` -### 深度优先遍历(迭代法) +#### 深度优先遍历(迭代法) 相关题解:[0094.二叉树的中序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0094.二叉树的中序遍历.md) @@ -170,7 +173,7 @@ vector postorderTraversal(TreeNode* root) { return result; } ``` -### 广度优先遍历(队列) +#### 广度优先遍历(队列) 相关题解:[0102.二叉树的层序遍历](https://programmercarl.com/0102.二叉树的层序遍历.html) @@ -208,7 +211,7 @@ vector> levelOrder(TreeNode* root) { * [0111.二叉树的最小深度(迭代法)](https://programmercarl.com/0111.二叉树的最小深度.html) * [0222.完全二叉树的节点个数(迭代法)](https://programmercarl.com/0222.完全二叉树的节点个数.html) -### 二叉树深度 +#### 二叉树深度 ```CPP int getDepth(TreeNode* node) { @@ -217,7 +220,7 @@ int getDepth(TreeNode* node) { } ``` -### 二叉树节点数量 +#### 二叉树节点数量 ```CPP int countNodes(TreeNode* root) { @@ -226,7 +229,7 @@ int countNodes(TreeNode* root) { } ``` -## 回溯算法 +### 回溯算法 ```CPP void backtracking(参数) { if (终止条件) { @@ -243,7 +246,7 @@ void backtracking(参数) { ``` -## 并查集 +### 并查集 ```CPP int n = 1005; // 根据题意而定 @@ -278,9 +281,9 @@ void backtracking(参数) { (持续补充ing) ## 其他语言版本 -JavaScript: +### JavaScript: -## 二分查找法 +#### 二分查找法 使用左闭右闭区间 @@ -322,7 +325,7 @@ var search = function (nums, target) { }; ``` -## KMP +#### KMP ```javascript var kmp = function (next, s) { @@ -340,9 +343,9 @@ var kmp = function (next, s) { } ``` -## 二叉树 +#### 二叉树 -### 深度优先遍历(递归) +##### 深度优先遍历(递归) 二叉树节点定义: @@ -387,7 +390,7 @@ var postorder = function (root, list) { } ``` -### 深度优先遍历(迭代) +##### 深度优先遍历(迭代) 前序遍历(中左右): @@ -447,7 +450,7 @@ var postorderTraversal = function (root) { }; ``` -### 广度优先遍历(队列) +##### 广度优先遍历(队列) ```javascript var levelOrder = function (root) { @@ -469,7 +472,7 @@ var levelOrder = function (root) { }; ``` -### 二叉树深度 +##### 二叉树深度 ```javascript var getDepth = function (node) { @@ -478,7 +481,7 @@ var getDepth = function (node) { } ``` -### 二叉树节点数量 +##### 二叉树节点数量 ```javascript var countNodes = function (root) { @@ -487,7 +490,7 @@ var countNodes = function (root) { } ``` -## 回溯算法 +#### 回溯算法 ```javascript function backtracking(参数) { @@ -505,7 +508,7 @@ function backtracking(参数) { ``` -## 并查集 +#### 并查集 ```javascript let n = 1005; // 根据题意而定 @@ -536,9 +539,9 @@ function backtracking(参数) { } ``` -TypeScript: +### TypeScript: -## 二分查找法 +#### 二分查找法 使用左闭右闭区间 @@ -580,7 +583,7 @@ var search = function (nums: number[], target: number): number { }; ``` -## KMP +#### KMP ```typescript var kmp = function (next: number[], s: number): void { @@ -598,9 +601,9 @@ var kmp = function (next: number[], s: number): void { } ``` -## 二叉树 +#### 二叉树 -### 深度优先遍历(递归) +##### 深度优先遍历(递归) 二叉树节点定义: @@ -650,7 +653,7 @@ var postorder = function (root: TreeNode | null, list: number[]): void { } ``` -### 深度优先遍历(迭代) +##### 深度优先遍历(迭代) 前序遍历(中左右): @@ -710,7 +713,7 @@ var postorderTraversal = function (root: TreeNode | null): number[] { }; ``` -### 广度优先遍历(队列) +##### 广度优先遍历(队列) ```typescript var levelOrder = function (root: TreeNode | null): number[] { @@ -732,7 +735,7 @@ var levelOrder = function (root: TreeNode | null): number[] { }; ``` -### 二叉树深度 +##### 二叉树深度 ```typescript var getDepth = function (node: TreNode | null): number { @@ -741,7 +744,7 @@ var getDepth = function (node: TreNode | null): number { } ``` -### 二叉树节点数量 +##### 二叉树节点数量 ```typescript var countNodes = function (root: TreeNode | null): number { @@ -750,7 +753,7 @@ var countNodes = function (root: TreeNode | null): number { } ``` -## 回溯算法 +#### 回溯算法 ```typescript function backtracking(参数) { @@ -768,7 +771,7 @@ function backtracking(参数) { ``` -## 并查集 +#### 并查集 ```typescript let n: number = 1005; // 根据题意而定 @@ -801,9 +804,9 @@ function backtracking(参数) { Java: +### Python: -Python: -## 二分查找法 +#### 二分查找法 ```python def binarysearch(nums, target): low = 0 @@ -823,7 +826,7 @@ def binarysearch(nums, target): return -1 ``` -## KMP +#### KMP ```python def kmp(self, a, s): diff --git a/problems/背包理论基础01背包-2.md b/problems/背包理论基础01背包-2.md index 019947e5..f60e261b 100644 --- a/problems/背包理论基础01背包-2.md +++ b/problems/背包理论基础01背包-2.md @@ -3,8 +3,10 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

+ # 动态规划:01背包理论基础(滚动数组) + ## 算法公开课 **[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html):[带你学透0-1背包问题!(滚动数组)](https://www.bilibili.com/video/BV1BU4y177kY/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 diff --git a/problems/链表总结篇.md b/problems/链表总结篇.md index dacd4dee..3f2f5c97 100644 --- a/problems/链表总结篇.md +++ b/problems/链表总结篇.md @@ -3,6 +3,7 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

+ # 链表总结篇