diff --git a/README.md b/README.md index 267d794e..8a3574fa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -👉 推荐 [在线阅读](http://programmercarl.com/) (Github在国内访问经常不稳定) +👉 推荐 [在线阅读](http://programmercarl.com/) (Github在国内访问经常不稳定) 👉 推荐 [Gitee同步](https://gitee.com/programmercarl/leetcode-master) > 1. **介绍**:本项目是一套完整的刷题计划,旨在帮助大家少走弯路,循序渐进学算法,[关注作者](#关于作者) @@ -494,6 +494,7 @@ ## 图论 * [463.岛屿的周长](./problems/0463.岛屿的周长.md) (模拟) * [841.钥匙和房间](./problems/0841.钥匙和房间.md) 【有向图】dfs,bfs都可以 +* [127.单词接龙](./problems/0127.单词接龙.md) 广搜 ## 并查集 * [684.冗余连接](./problems/0684.冗余连接.md) 【并查集基础题目】 diff --git a/problems/0001.两数之和.md b/problems/0001.两数之和.md index fd17af62..f12b5869 100644 --- a/problems/0001.两数之和.md +++ b/problems/0001.两数之和.md @@ -206,6 +206,23 @@ function twoSum(array $nums, int $target): array } ``` +Swift: +```swift +func twoSum(_ nums: [Int], _ target: Int) -> [Int] { + var res = [Int]() + var dict = [Int : Int]() + for i in 0 ..< nums.count { + let other = target - nums[i] + if dict.keys.contains(other) { + res.append(i) + res.append(dict[other]!) + return res + } + dict[nums[i]] = i + } + return res +} +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) diff --git a/problems/0027.移除元素.md b/problems/0027.移除元素.md index ff50d511..78f9afb7 100644 --- a/problems/0027.移除元素.md +++ b/problems/0027.移除元素.md @@ -246,6 +246,30 @@ func removeElement(_ nums: inout [Int], _ val: Int) -> Int { } ``` +PHP: +```php +class Solution { + /** + * @param Integer[] $nums + * @param Integer $val + * @return Integer + */ + function removeElement(&$nums, $val) { + if (count($nums) == 0) { + return 0; + } + // 快慢指针 + $slow = 0; + for ($fast = 0; $fast < count($nums); $fast++) { + if ($nums[$fast] != $val) { + $nums[$slow] = $nums[$fast]; + $slow++; + } + } + return $slow; + } +``` + C: ```c int removeElement(int* nums, int numsSize, int val){ diff --git a/problems/0046.全排列.md b/problems/0046.全排列.md index df9394eb..001c249e 100644 --- a/problems/0046.全排列.md +++ b/problems/0046.全排列.md @@ -183,6 +183,32 @@ class Solution { } } ``` +```java +// 解法2:通过判断path中是否存在数字,排除已经选择的数字 +class Solution { + List> result = new ArrayList<>(); + LinkedList path = new LinkedList<>(); + public List> permute(int[] nums) { + if (nums.length == 0) return result; + backtrack(nums, path); + return result; + } + public void backtrack(int[] nums, LinkedList path) { + if (path.size() == nums.length) { + result.add(new ArrayList<>(path)); + } + for (int i =0; i < nums.length; i++) { + // 如果path中已有,则跳过 + if (path.contains(nums[i])) { + continue; + } + path.add(nums[i]); + backtrack(nums, path); + path.removeLast(); + } + } +} +``` Python: ```python3 diff --git a/problems/0056.合并区间.md b/problems/0056.合并区间.md index 2322951a..82ca29e6 100644 --- a/problems/0056.合并区间.md +++ b/problems/0056.合并区间.md @@ -157,6 +157,28 @@ class Solution { } } ``` +```java +// 版本2 +class Solution { + public int[][] merge(int[][] intervals) { + LinkedList res = new LinkedList<>(); + Arrays.sort(intervals, (o1, o2) -> Integer.compare(o1[0], o2[0])); + res.add(intervals[0]); + for (int i = 1; i < intervals.length; i++) { + if (intervals[i][0] <= res.getLast()[1]) { + int start = res.getLast()[0]; + int end = Math.max(intervals[i][1], res.getLast()[1]); + res.removeLast(); + res.add(new int[]{start, end}); + } + else { + res.add(intervals[i]); + } + } + return res.toArray(new int[res.size()][]); + } +} +``` Python: ```python diff --git a/problems/0059.螺旋矩阵II.md b/problems/0059.螺旋矩阵II.md index 7d02ff15..3dbc2a50 100644 --- a/problems/0059.螺旋矩阵II.md +++ b/problems/0059.螺旋矩阵II.md @@ -191,33 +191,48 @@ class Solution { python: -```python +```python3 class Solution: + def generateMatrix(self, n: int) -> List[List[int]]: - left, right, up, down = 0, n-1, 0, n-1 - matrix = [ [0]*n for _ in range(n)] - num = 1 - while left<=right and up<=down: - # 填充左到右 - for i in range(left, right+1): - matrix[up][i] = num - num += 1 - up += 1 - # 填充上到下 - for i in range(up, down+1): - matrix[i][right] = num - num += 1 - right -= 1 - # 填充右到左 - for i in range(right, left-1, -1): - matrix[down][i] = num - num += 1 - down -= 1 - # 填充下到上 - for i in range(down, up-1, -1): - matrix[i][left] = num - num += 1 + # 初始化要填充的正方形 + matrix = [[0] * n for _ in range(n)] + + left, right, up, down = 0, n - 1, 0, n - 1 + number = 1 # 要填充的数字 + + while left < right and up < down: + + # 从左到右填充上边 + for x in range(left, right): + matrix[up][x] = number + number += 1 + + # 从上到下填充右边 + for y in range(up, down): + matrix[y][right] = number + number += 1 + + # 从右到左填充下边 + for x in range(right, left, -1): + matrix[down][x] = number + number += 1 + + # 从下到上填充左边 + for y in range(down, up, -1): + matrix[y][left] = number + number += 1 + + # 缩小要填充的范围 left += 1 + right -= 1 + up += 1 + down -= 1 + + # 如果阶数为奇数,额外填充一次中心 + if n % 2: + matrix[n // 2][n // 2] = number + return matrix ``` @@ -358,6 +373,100 @@ func generateMatrix(_ n: Int) -> [[Int]] { } ``` +Rust: + +```rust +impl Solution { + pub fn generate_matrix(n: i32) -> Vec> { + let mut res = vec![vec![0; n as usize]; n as usize]; + let (mut startX, mut startY, mut offset): (usize, usize, usize) = (0, 0, 1); + let mut loopIdx = n/2; + let mid: usize = loopIdx as usize; + let mut count = 1; + let (mut i, mut j): (usize, usize) = (0, 0); + while loopIdx > 0 { + i = startX; + j = startY; + + while j < (startY + (n as usize) - offset) { + res[i][j] = count; + count += 1; + j += 1; + } + + while i < (startX + (n as usize) - offset) { + res[i][j] = count; + count += 1; + i += 1; + } + + while j > startY { + res[i][j] = count; + count += 1; + j -= 1; + } + + while i > startX { + res[i][j] = count; + count += 1; + i -= 1; + } + + startX += 1; + startY += 1; + offset += 2; + loopIdx -= 1; + } + + if(n % 2 == 1) { + res[mid][mid] = count; + } + res + } +} +``` + +PHP: +```php +class Solution { + /** + * @param Integer $n + * @return Integer[][] + */ + function generateMatrix($n) { + // 初始化数组 + $res = array_fill(0, $n, array_fill(0, $n, 0)); + $mid = $loop = floor($n / 2); + $startX = $startY = 0; + $offset = 1; + $count = 1; + while ($loop > 0) { + $i = $startX; + $j = $startY; + for (; $j < $startY + $n - $offset; $j++) { + $res[$i][$j] = $count++; + } + for (; $i < $startX + $n - $offset; $i++) { + $res[$i][$j] = $count++; + } + for (; $j > $startY; $j--) { + $res[$i][$j] = $count++; + } + for (; $i > $startX; $i--) { + $res[$i][$j] = $count++; + } + $startX += 1; + $startY += 1; + $offset += 2; + $loop--; + } + if ($n % 2 == 1) { + $res[$mid][$mid] = $count; + } + return $res; + } +} +``` ----------------------- diff --git a/problems/0115.不同的子序列.md b/problems/0115.不同的子序列.md index a5162ae0..908682dd 100644 --- a/problems/0115.不同的子序列.md +++ b/problems/0115.不同的子序列.md @@ -221,6 +221,30 @@ class SolutionDP2: ``` Go: +```go +func numDistinct(s string, t string) int { + dp:= make([][]int,len(s)+1) + for i:=0;i + + + + +

+

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

+ +# 127. 单词接龙 + +[力扣题目链接](https://leetcode-cn.com/problems/word-ladder/) + + +字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列: +* 序列中第一个单词是 beginWord 。 +* 序列中最后一个单词是 endWord 。 +* 每次转换只能改变一个字母。 +* 转换过程中的中间单词必须是字典 wordList 中的单词。 +* 给你两个单词 beginWord 和 endWord 和一个字典 wordList ,找到从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。 + +  +示例 1: + +* 输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] +* 输出:5 +* 解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。 + +示例 2: +* 输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"] +* 输出:0 +* 解释:endWord "cog" 不在字典中,所以无法进行转换。 + + +# 思路 + +以示例1为例,从这个图中可以看出 hit 到 cog的路线,不止一条,有三条,两条是最短的长度为5,一条长度为6。 + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20210827175432.png) + +本题只需要求出最短长度就可以了,不用找出路径。 + +所以这道题要解决两个问题: + +* 图中的线是如何连在一起的 +* 起点和终点的最短路径长度 + + +首先题目中并没有给出点与点之间的连线,而是要我们自己去连,条件是字符只能差一个,所以判断点与点之间的关系,要自己判断是不是差一个字符,如果差一个字符,那就是有链接。 + +然后就是求起点和终点的最短路径长度,**这里无向图求最短路,广搜最为合适,广搜只要搜到了终点,那么一定是最短的路径**。因为广搜就是以起点中心向四周扩散的搜索。 + +本题如果用深搜,会非常麻烦。 + +另外需要有一个注意点: + +* 本题是一个无向图,需要用标记位,标记着节点是否走过,否则就会死循环! +* 本题给出集合是数组型的,可以转成set结构,查找更快一些 + +C++代码如下:(详细注释) + +```CPP +class Solution { +public: + int ladderLength(string beginWord, string endWord, vector& wordList) { + // 将vector转成unordered_set,提高查询速度 + unordered_set wordSet(wordList.begin(), wordList.end()); + // 如果endWord没有在wordSet出现,直接返回0 + if (wordSet.find(endWord) == wordSet.end()) return 0; + // 记录word是否访问过 + unordered_map visitMap; // + // 初始化队列 + queue que; + que.push(beginWord); + // 初始化visitMap + visitMap.insert(pair(beginWord, 1)); + + while(!que.empty()) { + string word = que.front(); + que.pop(); + int path = visitMap[word]; // 这个word的路径长度 + for (int i = 0; i < word.size(); i++) { + string newWord = word; // 用一个新单词替换word,因为每次置换一个字母 + for (int j = 0 ; j < 26; j++) { + newWord[i] = j + 'a'; + if (newWord == endWord) return path + 1; // 找到了end,返回path+1 + // wordSet出现了newWord,并且newWord没有被访问过 + if (wordSet.find(newWord) != wordSet.end() + && visitMap.find(newWord) == visitMap.end()) { + // 添加访问信息 + visitMap.insert(pair(newWord, path + 1)); + que.push(newWord); + } + } + } + } + return 0; + } +}; +``` + +# 其他语言版本 + +## Java + +```java +public int ladderLength(String beginWord, String endWord, List wordList) { + HashSet wordSet = new HashSet<>(wordList); //转换为hashset 加快速度 + if (wordSet.size() == 0 || !wordSet.contains(endWord)) { //特殊情况判断 + return 0; + } + Queue queue = new LinkedList<>(); //bfs 队列 + queue.offer(beginWord); + Map map = new HashMap<>(); //记录单词对应路径长度 + map.put(beginWord, 1); + + while (!queue.isEmpty()) { + String word = queue.poll(); //取出队头单词 + int path = map.get(word); //获取到该单词的路径长度 + for (int i = 0; i < word.length(); i++) { //遍历单词的每个字符 + char[] chars = word.toCharArray(); //将单词转换为char array,方便替换 + for (char k = 'a'; k <= 'z'; k++) { //从'a' 到 'z' 遍历替换 + chars[i] = k; //替换第i个字符 + String newWord = String.valueOf(chars); //得到新的字符串 + if (newWord.equals(endWord)) { //如果新的字符串值与endWord一致,返回当前长度+1 + return path + 1; + } + if (wordSet.contains(newWord) && !map.containsKey(newWord)) { //如果新单词在set中,但是没有访问过 + map.put(newWord, path + 1); //记录单词对应的路径长度 + queue.offer(newWord);//加入队尾 + } + } + } + } + return 0; //未找到 +} +``` + +## Python + +## Go + +## JavaScript + + +----------------------- +* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) +* B站视频:[代码随想录](https://space.bilibili.com/525438321) +* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) +
diff --git a/problems/0134.加油站.md b/problems/0134.加油站.md index ff3bc8b2..9aa88fce 100644 --- a/problems/0134.加油站.md +++ b/problems/0134.加油站.md @@ -200,6 +200,7 @@ public: Java: ```java +// 解法1 class Solution { public int canCompleteCircuit(int[] gas, int[] cost) { int sum = 0; @@ -221,7 +222,26 @@ class Solution { } } ``` - +```java +// 解法2 +class Solution { + public int canCompleteCircuit(int[] gas, int[] cost) { + int curSum = 0; + int totalSum = 0; + int index = 0; + for (int i = 0; i < gas.length; i++) { + curSum += gas[i] - cost[i]; + totalSum += gas[i] - cost[i]; + if (curSum < 0) { + index = (i + 1) % gas.length ; + curSum = 0; + } + } + if (totalSum < 0) return -1; + return index; + } +} +``` Python: ```python class Solution: diff --git a/problems/0150.逆波兰表达式求值.md b/problems/0150.逆波兰表达式求值.md index bcde7d5b..36652109 100644 --- a/problems/0150.逆波兰表达式求值.md +++ b/problems/0150.逆波兰表达式求值.md @@ -13,7 +13,7 @@ # 150. 逆波兰表达式求值 -https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/ +[力扣题目链接](https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/) 根据 逆波兰表示法,求表达式的值。 @@ -23,7 +23,7 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/ 整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。 -  + 示例 1: * 输入: ["2", "1", "+", "3", " * "] @@ -37,16 +37,21 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/ 示例 3: * 输入: ["10", "6", "9", "3", "+", "-11", " * ", "/", " * ", "17", "+", "5", "+"] + * 输出: 22 + * 解释:该算式转化为常见的中缀算术表达式为: + + ``` ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 -= ((10 * (6 / (12 * -11))) + 17) + 5 -= ((10 * (6 / -132)) + 17) + 5 -= ((10 * 0) + 17) + 5 -= (0 + 17) + 5 -= 17 + 5 -= 22 -  + = ((10 * (6 / (12 * -11))) + 17) + 5 + = ((10 * (6 / -132)) + 17) + 5 + = ((10 * 0) + 17) + 5 + = (0 + 17) + 5 + = 17 + 5 + = 22 + ``` + 逆波兰表达式:是一种后缀表达式,所谓后缀就是指算符写在后面。 @@ -62,7 +67,7 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/ # 思路 -在上一篇文章中[1047.删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)提到了 递归就是用栈来实现的。 +在上一篇文章中[1047.删除字符串中的所有相邻重复项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html)提到了 递归就是用栈来实现的。 所以**栈与递归之间在某种程度上是可以转换的!** 这一点我们在后续讲解二叉树的时候,会更详细的讲解到。 @@ -70,12 +75,12 @@ https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/ 但我们没有必要从二叉树的角度去解决这个问题,只要知道逆波兰表达式是用后续遍历的方式把二叉树序列化了,就可以了。 -在进一步看,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么**这岂不就是一个相邻字符串消除的过程,和[1047.删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)中的对对碰游戏是不是就非常像了。** +在进一步看,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么**这岂不就是一个相邻字符串消除的过程,和[1047.删除字符串中的所有相邻重复项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html)中的对对碰游戏是不是就非常像了。** 如动画所示: ![150.逆波兰表达式求值](https://code-thinking.cdn.bcebos.com/gifs/150.逆波兰表达式求值.gif) -相信看完动画大家应该知道,这和[1047. 删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/1-x6r1wGA9mqIHW5LrMvBg)是差不错的,只不过本题不要相邻元素做消除了,而是做运算! +相信看完动画大家应该知道,这和[1047. 删除字符串中的所有相邻重复项](https://programmercarl.com/1047.删除字符串中的所有相邻重复项.html)是差不错的,只不过本题不要相邻元素做消除了,而是做运算! C++代码如下: diff --git a/problems/0151.翻转字符串里的单词.md b/problems/0151.翻转字符串里的单词.md index 84e348a9..c4a9c7e0 100644 --- a/problems/0151.翻转字符串里的单词.md +++ b/problems/0151.翻转字符串里的单词.md @@ -467,6 +467,85 @@ function reverse(strArr, start, end) { } ``` +Swift: + +```swift +func reverseWords(_ s: String) -> String { + var stringArr = removeSpace(s) + reverseString(&stringArr, startIndex: 0, endIndex: stringArr.count - 1) + reverseWord(&stringArr) + return String(stringArr) +} + +/// 1、移除多余的空格(前后所有的空格,中间只留一个空格) +func removeSpace(_ s: String) -> [Character] { + let ch = Array(s) + var left = 0 + var right = ch.count - 1 + // 忽略字符串前面的所有空格 + while ch[left] == " " { + left += 1 + } + // 忽略字符串后面的所有空格 + while ch[right] == " " { + right -= 1 + } + + // 接下来就是要处理中间的多余空格 + var lastArr = Array() + while left <= right { + // 准备加到新字符串当中的字符 + let char = ch[left] + // 新的字符串的最后一个字符;或者原字符串中,准备加到新字符串的那个字符;这两个字符当中,只要有一个不是空格,就可以加到新的字符串当中 + if char != " " || lastArr[lastArr.count - 1] != " " { + lastArr.append(char) + } + + left += 1 + } + return lastArr +} + +/// 2、反转整个字符串 +func reverseString(_ s: inout [Character], startIndex: Int, endIndex: Int) { + var start = startIndex + var end = endIndex + while start < end { + (s[start], s[end]) = (s[end], s[start]) + start += 1 + end -= 1 + } +} + +/// 3、再次将字符串里面的单词反转 +func reverseWord(_ s: inout [Character]) { + var start = 0 + var end = 0 + var entry = false + + for i in 0.. Int { + var sum = 0 + var num = number + while num > 0 { + let temp = num % 10 + sum += (temp * temp) + num /= 10 + } + return sum +} +func isHappy(_ n: Int) -> Bool { + var set = Set() + var num = n + while true { + let sum = self.getSum(num) + if sum == 1 { + return true + } + // 如果这个sum曾经出现过,说明已经陷入了无限循环了 + if set.contains(sum) { + return false + } else { + set.insert(sum) + } + num = sum + } +} +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) diff --git a/problems/0205.同构字符串.md b/problems/0205.同构字符串.md index 5b0dd82b..5d20aa4a 100644 --- a/problems/0205.同构字符串.md +++ b/problems/0205.同构字符串.md @@ -97,6 +97,23 @@ class Solution { ## Go ```go +func isIsomorphic(s string, t string) bool { + map1 := make(map[byte]byte) + map2 := make(map[byte]byte) + for i := range s { + if _, ok := map1[s[i]]; !ok { + map1[s[i]] = t[i] // map1保存 s[i] 到 t[j]的映射 + } + if _, ok := map2[t[i]]; !ok { + map2[t[i]] = s[i] // map2保存 t[i] 到 s[j]的映射 + } + // 无法映射,返回 false + if (map1[s[i]] != t[i]) || (map2[t[i]] != s[i]) { + return false + } + } + return true +} ``` ## JavaScript diff --git a/problems/0209.长度最小的子数组.md b/problems/0209.长度最小的子数组.md index ceca8c87..7c3fd0e7 100644 --- a/problems/0209.长度最小的子数组.md +++ b/problems/0209.长度最小的子数组.md @@ -264,6 +264,34 @@ impl Solution { } ``` +PHP: +```php +// 双指针 - 滑动窗口 +class Solution { + /** + * @param Integer $target + * @param Integer[] $nums + * @return Integer + */ + function minSubArrayLen($target, $nums) { + if (count($nums) < 1) { + return 0; + } + $sum = 0; + $res = PHP_INT_MAX; + $left = 0; + for ($right = 0; $right < count($nums); $right++) { + $sum += $nums[$right]; + while ($sum >= $target) { + $res = min($res, $right - $left + 1); + $sum -= $nums[$left]; + $left++; + } + } + return $res == PHP_INT_MAX ? 0 : $res; + } +} +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) diff --git a/problems/0225.用队列实现栈.md b/problems/0225.用队列实现栈.md index ccf93f1f..8d4db953 100644 --- a/problems/0225.用队列实现栈.md +++ b/problems/0225.用队列实现栈.md @@ -12,7 +12,7 @@ # 225. 用队列实现栈 -https://leetcode-cn.com/problems/implement-stack-using-queues/ +[力扣题目链接](https://leetcode-cn.com/problems/implement-stack-using-queues/) 使用队列实现栈的下列操作: @@ -34,7 +34,7 @@ https://leetcode-cn.com/problems/implement-stack-using-queues/ 有的同学可能疑惑这种题目有什么实际工程意义,**其实很多算法题目主要是对知识点的考察和教学意义远大于其工程实践的意义,所以面试题也是这样!** -刚刚做过[栈与队列:我用栈来实现队列怎么样?](https://mp.weixin.qq.com/s/Cj6R0qu8rFA7Et9V_ZMjCA)的同学可能依然想着用一个输入队列,一个输出队列,就可以模拟栈的功能,仔细想一下还真不行! +刚刚做过[栈与队列:我用栈来实现队列怎么样?](https://programmercarl.com/0232.用栈实现队列.html)的同学可能依然想着用一个输入队列,一个输出队列,就可以模拟栈的功能,仔细想一下还真不行! **队列模拟栈,其实一个队列就够了**,那么我们先说一说两个队列来实现栈的思路。 @@ -359,6 +359,71 @@ class MyStack: Go: +```go +type MyStack struct { + queue []int//创建一个队列 +} + + +/** Initialize your data structure here. */ +func Constructor() MyStack { + return MyStack{ //初始化 + queue:make([]int,0), + } +} + + +/** Push element x onto stack. */ +func (this *MyStack) Push(x int) { + //添加元素 + this.queue=append(this.queue,x) +} + + +/** Removes the element on top of the stack and returns that element. */ +func (this *MyStack) Pop() int { + n:=len(this.queue)-1//判断长度 + for n!=0{ //除了最后一个,其余的都重新添加到队列里 + val:=this.queue[0] + this.queue=this.queue[1:] + this.queue=append(this.queue,val) + n-- + } + //弹出元素 + val:=this.queue[0] + this.queue=this.queue[1:] + return val + +} + + +/** Get the top element. */ +func (this *MyStack) Top() int { + //利用Pop函数,弹出来的元素重新添加 + val:=this.Pop() + this.queue=append(this.queue,val) + return val +} + + +/** Returns whether the stack is empty. */ +func (this *MyStack) Empty() bool { + return len(this.queue)==0 +} + + +/** + * Your MyStack object will be instantiated and called as such: + * obj := Constructor(); + * obj.Push(x); + * param_2 := obj.Pop(); + * param_3 := obj.Top(); + * param_4 := obj.Empty(); + */ +``` + + + javaScript: 使用数组(push, shift)模拟队列 diff --git a/problems/0232.用栈实现队列.md b/problems/0232.用栈实现队列.md index e8bb877e..9f6bb90f 100644 --- a/problems/0232.用栈实现队列.md +++ b/problems/0232.用栈实现队列.md @@ -11,7 +11,7 @@ # 232.用栈实现队列 -https://leetcode-cn.com/problems/implement-queue-using-stacks/ +[力扣题目链接](https://leetcode-cn.com/problems/implement-queue-using-stacks/) 使用栈实现队列的下列操作: @@ -205,33 +205,26 @@ class MyQueue: def pop(self) -> int: """ - 1. 检查如果out里面元素,则直接pop - 2. 如果out没有元素,就把in里面的元素(除了第一个)依次pop后装进out里面 - 3. 直接把in剩下的元素pop出来,就是queue头部的 + Removes the element from in front of queue and returns that element. """ - if self.empty: + if self.empty(): return None if self.stack_out: return self.stack_out.pop() else: - for i in range(1, len(self.stack_in)): + for i in range(len(self.stack_in)): self.stack_out.append(self.stack_in.pop()) - return self.stack_in.pop() + return self.stack_out.pop() def peek(self) -> int: """ - 1. 查out有没有元素,有就把最上面的返回 - 2. 如果out没有元素,就把in最下面的返回 + Get the front element. """ - if self.empty: - return None - - if self.stack_out: - return self.stack_out[-1] - else: - return self.stack_in[0] + ans = self.pop() + self.stack_out.append(ans) + return ans def empty(self) -> bool: diff --git a/problems/0234.回文链表.md b/problems/0234.回文链表.md index 04015a7f..631d2f6b 100644 --- a/problems/0234.回文链表.md +++ b/problems/0234.回文链表.md @@ -144,6 +144,75 @@ public: ## Java ```java +// 方法一,使用数组 +class Solution { + public boolean isPalindrome(ListNode head) { + int len = 0; + // 统计链表长度 + ListNode cur = head; + while (cur != null) { + len++; + cur = cur.next; + } + cur = head; + int[] res = new int[len]; + // 将元素加到数组之中 + for (int i = 0; i < res.length; i++){ + res[i] = cur.val; + cur = cur.next; + } + // 比较回文 + for (int i = 0, j = len - 1; i < j; i++, j--){ + if (res[i] != res[j]){ + return false; + } + } + return true; + } +} + +// 方法二,快慢指针 +class Solution { + public boolean isPalindrome(ListNode head) { + // 如果为空或者仅有一个节点,返回true + if (head == null && head.next == null) return true; + ListNode slow = head; + ListNode fast = head; + ListNode pre = head; + while (fast != null && fast.next != null){ + pre = slow; // 记录slow的前一个结点 + slow = slow.next; + fast = fast.next.next; + } + pre.next = null; // 分割两个链表 + + // 前半部分 + ListNode cur1 = head; + // 后半部分。这里使用了反转链表 + ListNode cur2 = reverseList(slow); + + while (cur1 != null){ + if (cur1.val != cur2.val) return false; + + // 注意要移动两个结点 + cur1 = cur1.next; + cur2 = cur2.next; + } + return true; + } + ListNode reverseList(ListNode head){ + // 反转链表 + ListNode tmp = null; + ListNode pre = null; + while (head != null){ + tmp = head.next; + head.next = pre; + pre = head; + head = tmp; + } + return pre; + } +} ``` ## Python @@ -209,11 +278,13 @@ class Solution: ## Go ```go + ``` ## JavaScript ```js + ``` diff --git a/problems/0235.二叉搜索树的最近公共祖先.md b/problems/0235.二叉搜索树的最近公共祖先.md index fab450ba..929e6eb2 100644 --- a/problems/0235.二叉搜索树的最近公共祖先.md +++ b/problems/0235.二叉搜索树的最近公共祖先.md @@ -7,7 +7,7 @@

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

-## 235. 二叉搜索树的最近公共祖先 +# 235. 二叉搜索树的最近公共祖先 [力扣题目链接](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/) @@ -21,14 +21,15 @@ 示例 1: -输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 -输出: 6 -解释: 节点 2 和节点 8 的最近公共祖先是 6。 +* 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 +* 输出: 6 +* 解释: 节点 2 和节点 8 的最近公共祖先是 6。 + 示例 2: -输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4 -输出: 2 -解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。 +* 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4 +* 输出: 2 +* 解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。 说明: @@ -36,7 +37,9 @@ * 所有节点的值都是唯一的。 * p、q 为不同节点且均存在于给定的二叉搜索树中。 -## 思路 +# 思路 + + 做过[二叉树:公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)题目的同学应该知道,利用回溯从底向上搜索,遇到一个节点的左子树里有p,右子树里有q,那么当前节点就是最近公共祖先。 @@ -58,6 +61,7 @@ 可以看出直接按照指定的方向,就可以找到节点4,为最近公共祖先,而且不需要遍历整棵树,找到结果直接返回! +## 递归法 递归三部曲如下: @@ -111,7 +115,6 @@ if (cur->val > p->val && cur->val > q->val) { ``` if (递归函数(root->left)) return ; - if (递归函数(root->right)) return ; ``` @@ -128,7 +131,7 @@ left与right的逻辑处理; 如果 cur->val 小于 p->val,同时 cur->val 小于 q->val,那么就应该向右遍历(目标区间在右子树)。 -``` +```CPP if (cur->val < p->val && cur->val < q->val) { TreeNode* right = traversal(cur->right, p, q); if (right != NULL) { @@ -140,9 +143,9 @@ if (cur->val < p->val && cur->val < q->val) { 剩下的情况,就是cur节点在区间(p->val <= cur->val && cur->val <= q->val)或者 (q->val <= cur->val && cur->val <= p->val)中,那么cur就是最近公共祖先了,直接返回cur。 代码如下: + ``` return cur; - ``` 那么整体递归代码如下: @@ -216,7 +219,7 @@ public: 灵魂拷问:是不是又被简单的迭代法感动到痛哭流涕? -## 总结 +# 总结 对于二叉搜索树的最近祖先问题,其实要比[普通二叉树公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)简单的多。 @@ -225,10 +228,15 @@ public: 最后给出了对应的迭代法,二叉搜索树的迭代法甚至比递归更容易理解,也是因为其有序性(自带方向性),按照目标区间找就行了。 -## 其他语言版本 +# 其他语言版本 -Java: +## Java + +递归法: + + +迭代法: ```java class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { @@ -246,15 +254,11 @@ class Solution { } ``` -Python: -```python -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, x): -# self.val = x -# self.left = None -# self.right = None +## Python + +递归法: +```python class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': if not root: return root //中 @@ -264,18 +268,14 @@ class Solution: return self.lowestCommonAncestor(root.right,p,q) //右 else: return root ``` -Go: -> BSL法 +迭代法: + + +## Go + +递归法: ```go -/** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode - * } - */ //利用BSL的性质(前序遍历有序) func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { if root==nil{return nil} @@ -287,34 +287,10 @@ func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { } ``` -> 普通法 -```go -/** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode - * } - */ -//递归会将值层层返回 -func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { - //终止条件 - if root==nil||root.Val==p.Val||root.Val==q.Val{return root}//最后为空或者找到一个值时,就返回这个值 - //后序遍历 - findLeft:=lowestCommonAncestor(root.Left,p,q) - findRight:=lowestCommonAncestor(root.Right,p,q) - //处理单层逻辑 - if findLeft!=nil&&findRight!=nil{return root}//说明在root节点的两边 - if findLeft==nil{//左边没找到,就说明在右边找到了 - return findRight - }else {return findLeft} -} -``` +## JavaScript -JavaScript版本: -1. 使用递归的方法 +递归法: ```javascript var lowestCommonAncestor = function(root, p, q) { // 使用递归的方法 @@ -336,7 +312,8 @@ var lowestCommonAncestor = function(root, p, q) { return root; }; ``` -2. 使用迭代的方法 + +迭代法 ```javascript var lowestCommonAncestor = function(root, p, q) { // 使用迭代的方法 @@ -355,7 +332,6 @@ var lowestCommonAncestor = function(root, p, q) { ``` - ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) diff --git a/problems/0236.二叉树的最近公共祖先.md b/problems/0236.二叉树的最近公共祖先.md index 16c235ee..59345a24 100644 --- a/problems/0236.二叉树的最近公共祖先.md +++ b/problems/0236.二叉树的最近公共祖先.md @@ -9,7 +9,7 @@ > 本来是打算将二叉树和二叉搜索树的公共祖先问题一起讲,后来发现篇幅过长了,只能先说一说二叉树的公共祖先问题。 -## 236. 二叉树的最近公共祖先 +# 236. 二叉树的最近公共祖先 [力扣题目链接](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/) @@ -35,7 +35,7 @@ * 所有节点的值都是唯一的。 * p、q 为不同节点且均存在于给定的二叉树中。 -## 思路 +# 思路 遇到这个题目首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。 @@ -202,7 +202,7 @@ public: }; ``` -## 总结 +# 总结 这道题目刷过的同学未必真正了解这里面回溯的过程,以及结果是如何一层一层传上去的。 @@ -219,10 +219,10 @@ public: 本题没有给出迭代法,因为迭代法不适合模拟回溯的过程。理解递归的解法就够了。 -## 其他语言版本 +# 其他语言版本 -Java: +## Java ```Java class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { @@ -261,14 +261,9 @@ class Solution { } ``` -Python: +## Python + ```python -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, x): -# self.val = x -# self.left = None -# self.right = None //递归 class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': @@ -280,7 +275,9 @@ class Solution: elif not left and right: return right //目标节点是通过right返回的 else: return None //没找到 ``` -Go: + +## Go + ```Go func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { // check @@ -310,7 +307,8 @@ func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { } ``` -JavaScript版本: +## JavaScript + ```javascript var lowestCommonAncestor = function(root, p, q) { // 使用递归的方法 diff --git a/problems/0279.完全平方数.md b/problems/0279.完全平方数.md index 3c0f0414..865669c2 100644 --- a/problems/0279.完全平方数.md +++ b/problems/0279.完全平方数.md @@ -9,7 +9,7 @@ ## 279.完全平方数 -题目地址:https://leetcode-cn.com/problems/perfect-squares/ +[力扣题目链接](https://leetcode-cn.com/problems/perfect-squares/) 给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。 @@ -36,7 +36,7 @@ **我来把题目翻译一下:完全平方数就是物品(可以无限件使用),凑个正整数n就是背包,问凑满这个背包最少有多少物品?** -感受出来了没,这么浓厚的完全背包氛围,而且和昨天的题目[动态规划:322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)就是一样一样的! +感受出来了没,这么浓厚的完全背包氛围,而且和昨天的题目[动态规划:322. 零钱兑换](https://programmercarl.com/0322.零钱兑换.html)就是一样一样的! 动规五部曲分析如下: @@ -70,7 +70,7 @@ dp[0]表示 和为0的完全平方数的最小数量,那么dp[0]一定是0。 如果求排列数就是外层for遍历背包,内层for循环遍历物品。 -在[动态规划:322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)中我们就深入探讨了这个问题,本题也是一样的,是求最小数! +在[动态规划:322. 零钱兑换](https://programmercarl.com/0322.零钱兑换.html)中我们就深入探讨了这个问题,本题也是一样的,是求最小数! **所以本题外层for遍历背包,里层for遍历物品,还是外层for遍历物品,内层for遍历背包,都是可以的!** @@ -146,7 +146,7 @@ public: ## 总结 -如果大家认真做了昨天的题目[动态规划:322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ),今天这道就非常简单了,一样的套路一样的味道。 +如果大家认真做了昨天的题目[动态规划:322. 零钱兑换](https://programmercarl.com/0322.零钱兑换.html),今天这道就非常简单了,一样的套路一样的味道。 但如果没有按照「代码随想录」的题目顺序来做的话,做动态规划或者做背包问题,上来就做这道题,那还是挺难的! diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md index 0ac6e784..f9145f4c 100644 --- a/problems/0300.最长上升子序列.md +++ b/problems/0300.最长上升子序列.md @@ -8,13 +8,13 @@ ## 300.最长递增子序列 -题目链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/ +[力扣题目链接](https://leetcode-cn.com/problems/longest-increasing-subsequence/) 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 -  + 示例 1: 输入:nums = [10,9,2,5,3,7,101,18] 输出:4 @@ -27,7 +27,7 @@ 示例 3: 输入:nums = [7,7,7,7,7,7,7] 输出:1 -  + 提示: * 1 <= nums.length <= 2500 diff --git a/problems/0337.打家劫舍III.md b/problems/0337.打家劫舍III.md index 2a69ac56..dfb8ba57 100644 --- a/problems/0337.打家劫舍III.md +++ b/problems/0337.打家劫舍III.md @@ -29,7 +29,7 @@ 与198.打家劫舍,213.打家劫舍II一样,关键是要讨论当前节点抢还是不抢。 -如果抢了当前节点,两个孩子就不是动,如果没抢当前节点,就可以考虑抢左右孩子(**注意这里说的是“考虑”**) +如果抢了当前节点,两个孩子就不能动,如果没抢当前节点,就可以考虑抢左右孩子(**注意这里说的是“考虑”**) ### 暴力递归 @@ -91,7 +91,7 @@ public: ### 动态规划 -在上面两种方法,其实对一个节点 投与不投得到的最大金钱都没有做记录,而是需要实时计算。 +在上面两种方法,其实对一个节点 偷与不偷得到的最大金钱都没有做记录,而是需要实时计算。 而动态规划其实就是使用状态转移容器来记录状态的变化,这里可以使用一个长度为2的数组,记录当前节点偷与不偷所得到的的最大金钱。 @@ -121,7 +121,7 @@ vector robTree(TreeNode* cur) { 2. 确定终止条件 -在遍历的过程中,如果遇到空间点的话,很明显,无论偷还是不偷都是0,所以就返回 +在遍历的过程中,如果遇到空节点的话,很明显,无论偷还是不偷都是0,所以就返回 ``` if (cur == NULL) return vector{0, 0}; ``` diff --git a/problems/0344.反转字符串.md b/problems/0344.反转字符串.md index 8925d0e1..a5f18d58 100644 --- a/problems/0344.反转字符串.md +++ b/problems/0344.反转字符串.md @@ -12,7 +12,7 @@ # 344.反转字符串 -https://leetcode-cn.com/problems/reverse-string/ +[力扣题目链接](https://leetcode-cn.com/problems/reverse-string/) 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。 @@ -55,7 +55,7 @@ https://leetcode-cn.com/problems/reverse-string/ 接下来再来讲一下如何解决反转字符串的问题。 -大家应该还记得,我们已经讲过了[206.反转链表](https://mp.weixin.qq.com/s/ckEvIVGcNLfrz6OLOMoT0A)。 +大家应该还记得,我们已经讲过了[206.反转链表](https://programmercarl.com/0206.翻转链表.html)。 在反转链表中,使用了双指针的方法。 @@ -63,7 +63,7 @@ https://leetcode-cn.com/problems/reverse-string/ 因为字符串也是一种数组,所以元素在内存中是连续分布,这就决定了反转链表和反转字符串方式上还是有所差异的。 -如果对数组和链表原理不清楚的同学,可以看这两篇,[关于链表,你该了解这些!](https://mp.weixin.qq.com/s/fDGMmLrW7ZHlzkzlf_dZkw),[必须掌握的数组理论知识](https://mp.weixin.qq.com/s/c2KABb-Qgg66HrGf8z-8Og)。 +如果对数组和链表原理不清楚的同学,可以看这两篇,[关于链表,你该了解这些!](https://programmercarl.com/链表理论基础.html),[必须掌握的数组理论知识](https://programmercarl.com/数组理论基础.html)。 对于字符串,我们定义两个指针(也可以说是索引下表),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。 diff --git a/problems/0349.两个数组的交集.md b/problems/0349.两个数组的交集.md index 62abf639..752eee51 100644 --- a/problems/0349.两个数组的交集.md +++ b/problems/0349.两个数组的交集.md @@ -14,7 +14,7 @@ ## 349. 两个数组的交集 -https://leetcode-cn.com/problems/intersection-of-two-arrays/ +[力扣题目链接](https://leetcode-cn.com/problems/intersection-of-two-arrays/) 题意:给定两个数组,编写一个函数来计算它们的交集。 @@ -32,7 +32,7 @@ https://leetcode-cn.com/problems/intersection-of-two-arrays/ 这道题用暴力的解法时间复杂度是O(n^2),那来看看使用哈希法进一步优化。 -那么用数组来做哈希表也是不错的选择,例如[242. 有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA) +那么用数组来做哈希表也是不错的选择,例如[242. 有效的字母异位词](https://programmercarl.com/0242.有效的字母异位词.html) 但是要注意,**使用数组来做哈希的题目,是因为题目都限制了数值的大小。** @@ -192,6 +192,22 @@ var intersection = function(nums1, nums2) { }; ``` +Swift: +```swift +func intersection(_ nums1: [Int], _ nums2: [Int]) -> [Int] { + var set1 = Set() + var set2 = Set() + for num in nums1 { + set1.insert(num) + } + for num in nums2 { + if set1.contains(num) { + set2.insert(num) + } + } + return Array(set2) +} +``` ## 相关题目 diff --git a/problems/0392.判断子序列.md b/problems/0392.判断子序列.md index 54c16489..784e3bbc 100644 --- a/problems/0392.判断子序列.md +++ b/problems/0392.判断子序列.md @@ -203,6 +203,25 @@ const isSubsequence = (s, t) => { }; ``` +Go: +```go +func isSubsequence(s string, t string) bool { + dp := make([][]int,len(s)+1) + for i:=0;i a - b) s = s.sort((a, b) => a - b) diff --git a/problems/0494.目标和.md b/problems/0494.目标和.md index 63ace2a7..342c229d 100644 --- a/problems/0494.目标和.md +++ b/problems/0494.目标和.md @@ -128,7 +128,10 @@ x = (S + sum) / 2 if ((S + sum) % 2 == 1) return 0; // 此时没有方案 ``` -**看到这种表达式,应该本能的反应,两个int相加数值可能溢出的问题,当然本题并没有溢出**。 +同时如果 S的绝对值已经大于sum,那么也是没有方案的。 +```CPP +if (abs(S) > sum) return 0; // 此时没有方案 +``` 再回归到01背包问题,为什么是01背包呢? @@ -200,7 +203,7 @@ public: int findTargetSumWays(vector& nums, int S) { int sum = 0; for (int i = 0; i < nums.size(); i++) sum += nums[i]; - if (S > sum) return 0; // 此时没有方案 + if (abs(S) > sum) return 0; // 此时没有方案 if ((S + sum) % 2 == 1) return 0; // 此时没有方案 int bagSize = (S + sum) / 2; vector dp(bagSize + 1, 0); diff --git a/problems/0501.二叉搜索树中的众数.md b/problems/0501.二叉搜索树中的众数.md index 4221e285..aeddc600 100644 --- a/problems/0501.二叉搜索树中的众数.md +++ b/problems/0501.二叉搜索树中的众数.md @@ -9,7 +9,7 @@ > 二叉树上应该怎么求,二叉搜索树上又应该怎么求? -## 501.二叉搜索树中的众数 +# 501.二叉搜索树中的众数 [力扣题目链接](https://leetcode-cn.com/problems/find-mode-in-binary-search-tree/solution/) @@ -33,7 +33,7 @@ 进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内) -## 思路 +# 思路 这道题目呢,递归法我从两个维度来讲。 @@ -321,7 +321,7 @@ public: }; ``` -## 总结 +# 总结 本题在递归法中,我给出了如果是普通二叉树,应该怎么求众数。 @@ -340,12 +340,13 @@ public: > **需要强调的是 leetcode上的耗时统计是非常不准确的,看个大概就行,一样的代码耗时可以差百分之50以上**,所以leetcode的耗时统计别太当回事,知道理论上的效率优劣就行了。 -## 其他语言版本 +# 其他语言版本 -Java: +## Java 暴力法 + ```java class Solution { public int[] findMode(FindModeInBinarySearchTree.TreeNode root) { @@ -379,6 +380,8 @@ class Solution { } ``` +中序遍历-不使用额外空间,利用二叉搜索树特性 + ```Java class Solution { ArrayList resList; @@ -427,15 +430,11 @@ class Solution { } ``` -Python: +## Python + +递归法 + ```python -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -# 递归法 class Solution: def findMode(self, root: TreeNode) -> List[int]: if not root: return @@ -460,36 +459,11 @@ class Solution: return findNumber(root) return self.res +``` -# 迭代法-中序遍历-使用额外空间map的方法: -class Solution: - def findMode(self, root: TreeNode) -> List[int]: - stack = [] - cur = root - pre = None - dist = {} - while cur or stack: - if cur: # 指针来访问节点,访问到最底层 - stack.append(cur) - cur = cur.left - else: # 逐一处理节点 - cur = stack.pop() - if cur.val in dist: - dist[cur.val] += 1 - else: - dist[cur.val] = 1 - pre = cur - cur = cur.right - - # 找出字典中最大的key - res = [] - for key, value in dist.items(): - if (value == max(dist.values())): - res.append(key) - return res - -# 迭代法-中序遍历-不使用额外空间,利用二叉搜索树特性: +迭代法-中序遍历-不使用额外空间,利用二叉搜索树特性 +```python class Solution: def findMode(self, root: TreeNode) -> List[int]: stack = [] @@ -521,18 +495,11 @@ class Solution: return res ``` -Go: +## Go + 暴力法(非BSL) ```go -/** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode - * } - */ func findMode(root *TreeNode) []int { var history map[int]int var maxValue int @@ -571,15 +538,7 @@ func traversal(root *TreeNode,history map[int]int){ 计数法,不使用额外空间,利用二叉树性质,中序遍历 ```go -/** - * Definition for a binary tree node. - * type TreeNode struct { - * Val int - * Left *TreeNode - * Right *TreeNode - * } - */ - func findMode(root *TreeNode) []int { +func findMode(root *TreeNode) []int { res := make([]int, 0) count := 1 max := 1 @@ -611,8 +570,9 @@ func traversal(root *TreeNode,history map[int]int){ } ``` -JavaScript版本: -使用额外空间map的方法: +## JavaScript + +使用额外空间map的方法 ```javascript var findMode = function(root) { // 使用递归中序遍历 @@ -649,8 +609,10 @@ var findMode = function(root) { } return res; }; -``` +``` + 不使用额外空间,利用二叉树性质,中序遍历(有序): + ```javascript var findMode = function(root) { // 不使用额外空间,使用中序遍历,设置出现最大次数初始值为1 diff --git a/problems/0530.二叉搜索树的最小绝对差.md b/problems/0530.二叉搜索树的最小绝对差.md index 46f6b796..ae6719ec 100644 --- a/problems/0530.二叉搜索树的最小绝对差.md +++ b/problems/0530.二叉搜索树的最小绝对差.md @@ -265,7 +265,7 @@ func getMinimumDifference(root *TreeNode) int { ``` ## JavaScript - +递归 先转换为有序数组 ```javascript /** * Definition for a binary tree node. @@ -297,6 +297,47 @@ var getMinimumDifference = function (root) { return diff; }; ``` +递归 在递归的过程中更新最小值 +```js +var getMinimumDifference = function(root) { + let res = Infinity + let preNode = null + // 中序遍历 + const inorder = (node) => { + if(!node) return + inorder(node.left) + // 更新res + if(preNode) res = Math.min(res, node.val - preNode.val) + // 记录前一个节点 + preNode = node + inorder(node.right) + } + inorder(root) + return res +} +``` + +迭代 中序遍历 +```js +var getMinimumDifference = function(root) { + let stack = [] + let cur = root + let res = Infinity + let pre = null + while(cur || stack.length) { + if(cur) { + stack.push(cur) + cur = cur.left + } else { + cur = stack.pop() + if(pre) res = Math.min(res, cur.val - pre.val) + pre = cur + cur = cur.right + } + } + return res +} +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) diff --git a/problems/0541.反转字符串II.md b/problems/0541.反转字符串II.md index 914fba23..df2fade8 100644 --- a/problems/0541.反转字符串II.md +++ b/problems/0541.反转字符串II.md @@ -12,7 +12,7 @@ # 541. 反转字符串II -https://leetcode-cn.com/problems/reverse-string-ii/ +[力扣题目链接](https://leetcode-cn.com/problems/reverse-string-ii/) 给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。 @@ -65,7 +65,7 @@ public: }; ``` -那么我们也可以实现自己的reverse函数,其实和题目[344. 反转字符串](https://mp.weixin.qq.com/s/_rNm66OJVl92gBDIbGpA3w)道理是一样的。 +那么我们也可以实现自己的reverse函数,其实和题目[344. 反转字符串](https://programmercarl.com/0344.反转字符串.html)道理是一样的。 下面我实现的reverse函数区间是左闭右闭区间,代码如下: @@ -254,6 +254,28 @@ var reverseStr = function(s, k) { ``` +Swift: + +```swift +func reverseStr(_ s: String, _ k: Int) -> String { + var ch = Array(s) + + for i in stride(from: 0, to: ch.count, by: 2 * k) { + var left = i + var right = min(s.count - 1, left + k - 1) + + while left < right { + (ch[left], ch[right]) = (ch[right], ch[left]) + left += 1 + right -= 1 + } + } + return String(ch) +} +``` + + + ----------------------- diff --git a/problems/0583.两个字符串的删除操作.md b/problems/0583.两个字符串的删除操作.md index 89a8f57c..91b07ca9 100644 --- a/problems/0583.两个字符串的删除操作.md +++ b/problems/0583.两个字符串的删除操作.md @@ -147,8 +147,38 @@ class Solution: ``` Go: +```go +func minDistance(word1 string, word2 string) int { + dp := make([][]int, len(word1)+1) + for i := 0; i < len(dp); i++ { + dp[i] = make([]int, len(word2)+1) + } + //初始化 + for i := 0; i < len(dp); i++ { + dp[i][0] = i + } + for j := 0; j < len(dp[0]); j++ { + dp[0][j] = j + } + for i := 1; i < len(dp); i++ { + for j := 1; j < len(dp[i]); j++ { + if word1[i-1] == word2[j-1] { + dp[i][j] = dp[i-1][j-1] + } else { + dp[i][j] = min(min(dp[i-1][j]+1, dp[i][j-1]+1), dp[i-1][j-1]+2) + } + } + } + return dp[len(dp)-1][len(dp[0])-1] +} - +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` Javascript: ```javascript const minDistance = (word1, word2) => { diff --git a/problems/0673.最长递增子序列的个数.md b/problems/0673.最长递增子序列的个数.md index ce3e8639..b3907e0e 100644 --- a/problems/0673.最长递增子序列的个数.md +++ b/problems/0673.最长递增子序列的个数.md @@ -9,6 +9,10 @@ # 673.最长递增子序列的个数 + +[力扣题目链接](https://leetcode-cn.com/problems/number-of-longest-increasing-subsequence/) + + 给定一个未排序的整数数组,找到最长递增子序列的个数。 示例 1: @@ -224,16 +228,110 @@ public: ## Java ```java +class Solution { + public int findNumberOfLIS(int[] nums) { + if (nums.length <= 1) return nums.length; + int[] dp = new int[nums.length]; + for(int i = 0; i < dp.length; i++) dp[i] = 1; + int[] count = new int[nums.length]; + for(int i = 0; i < count.length; i++) count[i] = 1; + + int maxCount = 0; + for (int i = 1; i < nums.length; i++) { + for (int j = 0; j < i; j++) { + if (nums[i] > nums[j]) { + if (dp[j] + 1 > dp[i]) { + dp[i] = dp[j] + 1; + count[i] = count[j]; + } else if (dp[j] + 1 == dp[i]) { + count[i] += count[j]; + } + } + if (dp[i] > maxCount) maxCount = dp[i]; + } + } + int result = 0; + for (int i = 0; i < nums.length; i++) { + if (maxCount == dp[i]) result += count[i]; + } + return result; + } +} ``` ## Python ```python +class Solution: + def findNumberOfLIS(self, nums: List[int]) -> int: + size = len(nums) + if size<= 1: return size + + dp = [1 for i in range(size)] + count = [1 for i in range(size)] + + maxCount = 0 + for i in range(1, size): + for j in range(i): + if nums[i] > nums[j]: + if dp[j] + 1 > dp[i] : + dp[i] = dp[j] + 1 + count[i] = count[j] + elif dp[j] + 1 == dp[i] : + count[i] += count[j] + if dp[i] > maxCount: + maxCount = dp[i]; + result = 0 + for i in range(size): + if maxCount == dp[i]: + result += count[i] + return result; ``` ## Go ```go + +func findNumberOfLIS(nums []int) int { + size := len(nums) + if size <= 1 { + return size + } + + dp := make([]int, size); + for i, _ := range dp { + dp[i] = 1 + } + count := make([]int, size); + for i, _ := range count { + count[i] = 1 + } + + maxCount := 0 + for i := 1; i < size; i++ { + for j := 0; j < i; j++ { + if nums[i] > nums[j] { + if dp[j] + 1 > dp[i] { + dp[i] = dp[j] + 1 + count[i] = count[j] + } else if dp[j] + 1 == dp[i] { + count[i] += count[j] + } + } + if dp[i] > maxCount { + maxCount = dp[i] + } + } + } + + result := 0 + for i := 0; i < size; i++ { + if maxCount == dp[i] { + result += count[i] + } + } + return result +} ``` ## JavaScript diff --git a/problems/0704.二分查找.md b/problems/0704.二分查找.md index 495f7367..f358d2be 100644 --- a/problems/0704.二分查找.md +++ b/problems/0704.二分查找.md @@ -478,6 +478,38 @@ int search(int* nums, int numsSize, int target){ } ``` +**PHP:** +```php +// 左闭右闭区间 +class Solution { + /** + * @param Integer[] $nums + * @param Integer $target + * @return Integer + */ + function search($nums, $target) { + if (count($nums) == 0) { + return -1; + } + $left = 0; + $right = count($nums) - 1; + while ($left <= $right) { + $mid = floor(($left + $right) / 2); + if ($nums[$mid] == $target) { + return $mid; + } + if ($nums[$mid] > $target) { + $right = $mid - 1; + } + else { + $left = $mid + 1; + } + } + return -1; + } +} +``` + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) diff --git a/problems/0714.买卖股票的最佳时机含手续费(动态规划).md b/problems/0714.买卖股票的最佳时机含手续费(动态规划).md index 50db8868..7c54a2fe 100644 --- a/problems/0714.买卖股票的最佳时机含手续费(动态规划).md +++ b/problems/0714.买卖股票的最佳时机含手续费(动态规划).md @@ -152,6 +152,25 @@ class Solution: ``` Go: +```Go +func maxProfit(prices []int, fee int) int { + n := len(prices) + dp := make([][2]int, n) + dp[0][0] = -prices[0] + for i := 1; i < n; i++ { + dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i]-fee) + dp[i][0] = max(dp[i-1][0], dp[i-1][1]-prices[i]) + } + return dp[n-1][1] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` Javascript: ```javascript diff --git a/problems/0968.监控二叉树.md b/problems/0968.监控二叉树.md index 737e92a0..a5fa71a7 100644 --- a/problems/0968.监控二叉树.md +++ b/problems/0968.监控二叉树.md @@ -368,7 +368,34 @@ class Solution: return result ``` Go: +```go +const inf = math.MaxInt64 / 2 +func minCameraCover(root *TreeNode) int { + var dfs func(*TreeNode) (a, b, c int) + dfs = func(node *TreeNode) (a, b, c int) { + if node == nil { + return inf, 0, 0 + } + lefta, leftb, leftc := dfs(node.Left) + righta, rightb, rightc := dfs(node.Right) + a = leftc + rightc + 1 + b = min(a, min(lefta+rightb, righta+leftb)) + c = min(a, leftb+rightb) + return + } + _, ans, _ := dfs(root) + return ans +} + +func min(a, b int) int { + if a <= b { + return a + } + return b +} + +``` Javascript: ```Javascript var minCameraCover = function(root) { diff --git a/problems/0977.有序数组的平方.md b/problems/0977.有序数组的平方.md index 089884fc..a57af3c1 100644 --- a/problems/0977.有序数组的平方.md +++ b/problems/0977.有序数组的平方.md @@ -270,6 +270,7 @@ def sorted_squares(nums) end ``` + C: ```c int* sortedSquares(int* nums, int numsSize, int* returnSize){ @@ -303,6 +304,37 @@ int* sortedSquares(int* nums, int numsSize, int* returnSize){ } ``` +PHP: +```php +class Solution { + /** + * @param Integer[] $nums + * @return Integer[] + */ + function sortedSquares($nums) { + // 双指针法 + $res = []; + for ($i = 0; $i < count($nums); $i++) { + $res[$i] = 0; + } + $k = count($nums) - 1; + for ($i = 0, $j = count($nums) - 1; $i <= $j; ) { + if ($nums[$i] ** 2 < $nums[$j] ** 2) { + $res[$k--] = $nums[$j] ** 2; + $j--; + } + else { + $res[$k--] = $nums[$i] ** 2; + $i++; + } + } + return $res; + } +} +``` + + + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) diff --git a/problems/1002.查找常用字符.md b/problems/1002.查找常用字符.md index c165f985..c0ca578e 100644 --- a/problems/1002.查找常用字符.md +++ b/problems/1002.查找常用字符.md @@ -10,7 +10,7 @@ # 1002. 查找常用字符 -https://leetcode-cn.com/problems/find-common-characters/ +[力扣题目链接](https://leetcode-cn.com/problems/find-common-characters/) 给定仅有小写字母组成的字符串数组 A,返回列表中的每个字符串中都显示的全部字符(包括重复字符)组成的列表。例如,如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 次。 @@ -23,7 +23,7 @@ https://leetcode-cn.com/problems/find-common-characters/ 【示例二】 输入:["cool","lock","cook"] 输出:["c","o"] -  + # 思路 @@ -40,9 +40,9 @@ https://leetcode-cn.com/problems/find-common-characters/ 可以看出这是指数级别的时间复杂度,非常高,而且代码实现也不容易,因为要统计 重复的字符,还要适当的替换或者去重。 -那我们还是哈希法吧。如果对哈希法不了解,可以看这篇:[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/RSUANESA_tkhKhYe3ZR8Jg)。 +那我们还是哈希法吧。如果对哈希法不了解,可以看这篇:[关于哈希表,你该了解这些!](https://programmercarl.com/哈希表理论基础.html)。 -如果对用数组来做哈希法不了解的话,可以看这篇:[把数组当做哈希表来用,很巧妙!](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)。 +如果对用数组来做哈希法不了解的话,可以看这篇:[把数组当做哈希表来用,很巧妙!](https://programmercarl.com/0242.有效的字母异位词.html)。 了解了哈希法,理解了数组在哈希法中的应用之后,可以来看解题思路了。 @@ -268,6 +268,47 @@ func min(a,b int)int{ return a } ``` + +Swift: +```swift +func commonChars(_ words: [String]) -> [String] { + var res = [String]() + if words.count < 1 { + return res + } + let aUnicodeScalarValue = "a".unicodeScalars.first!.value + let lettersMaxCount = 26 + // 用于统计所有字符串每个字母出现的 最小 频率 + var hash = Array(repeating: 0, count: lettersMaxCount) + // 统计第一个字符串每个字母出现的次数 + for unicodeScalar in words.first!.unicodeScalars { + hash[Int(unicodeScalar.value - aUnicodeScalarValue)] += 1 + } + // 统计除第一个字符串每个字母出现的次数 + for idx in 1 ..< words.count { + var hashOtherStr = Array(repeating: 0, count: lettersMaxCount) + for unicodeScalar in words[idx].unicodeScalars { + hashOtherStr[Int(unicodeScalar.value - aUnicodeScalarValue)] += 1 + } + // 更新hash,保证hash里统计的字母为出现的最小频率 + for k in 0 ..< lettersMaxCount { + hash[k] = min(hash[k], hashOtherStr[k]) + } + } + // 将hash统计的字符次数,转成输出形式 + for i in 0 ..< lettersMaxCount { + while hash[i] != 0 { // 注意这里是while,多个重复的字符 + let currentUnicodeScalarValue: UInt32 = UInt32(i) + aUnicodeScalarValue + let currentUnicodeScalar: UnicodeScalar = UnicodeScalar(currentUnicodeScalarValue)! + let outputStr = String(currentUnicodeScalar) // UnicodeScalar -> String + res.append(outputStr) + hash[i] -= 1 + } + } + return res +} +``` + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) diff --git a/problems/1005.K次取反后最大化的数组和.md b/problems/1005.K次取反后最大化的数组和.md index 8bdd0f41..020476a9 100644 --- a/problems/1005.K次取反后最大化的数组和.md +++ b/problems/1005.K次取反后最大化的数组和.md @@ -110,18 +110,16 @@ class Solution { int len = nums.length; for (int i = 0; i < len; i++) { //从前向后遍历,遇到负数将其变为正数,同时K-- - if (nums[i] < 0 && k > 0) { + if (nums[i] < 0 && K > 0) { nums[i] = -nums[i]; - k--; + K--; } } // 如果K还大于0,那么反复转变数值最小的元素,将K用完 - if (k % 2 == 1) nums[len - 1] = -nums[len - 1]; - int result = 0; - for (int a : nums) { - result += a; - } - return result; + + if (K % 2 == 1) nums[len - 1] = -nums[len - 1]; + return Arrays.stream(nums).sum(); + } } ``` diff --git a/problems/1047.删除字符串中的所有相邻重复项.md b/problems/1047.删除字符串中的所有相邻重复项.md index b60a8d1d..b88fd618 100644 --- a/problems/1047.删除字符串中的所有相邻重复项.md +++ b/problems/1047.删除字符串中的所有相邻重复项.md @@ -13,7 +13,7 @@ # 1047. 删除字符串中的所有相邻重复项 -https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/ +[力扣题目链接](https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/) 给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。 @@ -26,7 +26,7 @@ https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/ * 输入:"abbaca" * 输出:"ca" * 解释:例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。 -  + 提示: * 1 <= S.length <= 20000 diff --git a/problems/二叉树理论基础.md b/problems/二叉树理论基础.md index edd1fed4..94a0d67f 100644 --- a/problems/二叉树理论基础.md +++ b/problems/二叉树理论基础.md @@ -215,7 +215,7 @@ class TreeNode: ``` Go: -``` +```go type TreeNode struct { Val int Left *TreeNode @@ -224,7 +224,7 @@ type TreeNode struct { ``` JavaScript: -``` +```javascript function TreeNode(val, left, right) { this.val = (val===undefined ? 0 : val) this.left = (left===undefined ? null : left) diff --git a/problems/其他/参与本项目.md b/problems/其他/参与本项目.md index 69cb8555..cfa75439 100644 --- a/problems/其他/参与本项目.md +++ b/problems/其他/参与本项目.md @@ -5,3 +5,7 @@ **push代码之前 一定要 先pull最新代码**,否则提交的pr可能会有删除其他录友代码的操作。 一个pr 不要修改过多文件,因为一旦有一个 文件修改有问题,就不能合入,影响其他文件的合入了。 + +git add之前,要git diff 查看一下,本次提交所修改的代码是不是 自己修改的,是否 误删,或者误加的文件。 + +提交代码,不要使用git push -f 这种命令,要足够了解 -f 意味着什么。 diff --git a/problems/前序/广州互联网公司总结.md b/problems/前序/广州互联网公司总结.md index 3cc18c8e..ae41c899 100644 --- a/problems/前序/广州互联网公司总结.md +++ b/problems/前序/广州互联网公司总结.md @@ -51,7 +51,7 @@ ## 总结 -同在广东省,难免不了要和深圳对比,大家如果看了这篇:[深圳原来有这么多互联网公司,你都知道么?](https://mp.weixin.qq.com/s/3VJHF2zNohBwDBxARFIn-Q)就能感受到鲜明的对比了。 +同在广东省,难免不了要和深圳对比,大家如果看了这篇:[深圳原来有这么多互联网公司,你都知道么?](https://programmercarl.com/前序/深圳互联网公司总结.html)就能感受到鲜明的对比了。 广州大厂高端岗位其实比较少,本土只有微信和网易,微信呢毕竟还是腾讯的分部,而网易被很多人认为是杭州企业,其实网易总部在广州。 diff --git a/problems/剑指Offer05.替换空格.md b/problems/剑指Offer05.替换空格.md index 3294a16b..47907319 100644 --- a/problems/剑指Offer05.替换空格.md +++ b/problems/剑指Offer05.替换空格.md @@ -264,6 +264,49 @@ javaScript: }; ``` +Swift: + +```swift +func replaceSpace(_ s: String) -> String { + var strArr = Array(s) + var count = 0 + + // 统计空格的个数 + for i in strArr { + if i == " " { + count += 1 + } + } + // left 指向旧数组的最后一个元素 + var left = strArr.count - 1 + // right 指向扩容后数组的最后一个元素(这里还没对数组进行实际上的扩容) + var right = strArr.count + count * 2 - 1 + + // 实际对数组扩容 + for _ in 0..<(count * 2) { + strArr.append(" ") + } + + while left < right { + if strArr[left] == " " { + strArr[right] = "0" + strArr[right - 1] = "2" + strArr[right - 2] = "%" + left -= 1 + right -= 3 + } else { + strArr[right] = strArr[left] + left -= 1 + right -= 1 + } + } + + return String(strArr) +} +``` + + + ----------------------- diff --git a/problems/剑指Offer58-II.左旋转字符串.md b/problems/剑指Offer58-II.左旋转字符串.md index f4d3368c..d8aaca67 100644 --- a/problems/剑指Offer58-II.左旋转字符串.md +++ b/problems/剑指Offer58-II.左旋转字符串.md @@ -214,6 +214,34 @@ var reverseLeftWords = function (s, n) { }; ``` +Swift: + +```swift +func reverseLeftWords(_ s: String, _ n: Int) -> String { + var ch = Array(s) + let len = ch.count + // 反转区间[0, n - 1] + reverseString(&ch, startIndex: 0, endIndex: n - 1) + // 反转区间[n, len - 1] + reverseString(&ch, startIndex: n, endIndex: len - 1) + // 反转区间[0, len - 1],也就是整个字符串反转 + reverseString(&ch, startIndex: 0, endIndex: len - 1) + return String(ch) +} + +func reverseString(_ s: inout [Character], startIndex: Int, endIndex: Int) { + var start = startIndex + var end = endIndex + while start < end { + (s[start], s[end]) = (s[end], s[start]) + start += 1 + end -= 1 + } +} +``` + + + diff --git a/problems/周总结/20210107动规周末总结.md b/problems/周总结/20210107动规周末总结.md index 67957f05..b4baa4ad 100644 --- a/problems/周总结/20210107动规周末总结.md +++ b/problems/周总结/20210107动规周末总结.md @@ -3,7 +3,7 @@ ## 周一 -在[关于动态规划,你该了解这些!](https://mp.weixin.qq.com/s/ocZwfPlCWrJtVGACqFNAag)中我们讲解了动态规划的基础知识。 +在[关于动态规划,你该了解这些!](https://programmercarl.com/动态规划理论基础.html)中我们讲解了动态规划的基础知识。 首先讲一下动规和贪心的区别,其实大家不用太强调理论上的区别,做做题,就感受出来了。 @@ -33,13 +33,13 @@ ## 周二 -这道题目[动态规划:斐波那契数](https://mp.weixin.qq.com/s/ko0zLJplF7n_4TysnPOa_w)是当之无愧的动规入门题。 +这道题目[动态规划:斐波那契数](https://programmercarl.com/0509.斐波那契数.html)是当之无愧的动规入门题。 简单题,我们就是用来了解方法论的,用动规五部曲走一遍,题目其实已经把递推公式,和dp数组如何初始化都给我们了。 ## 周三 -[动态规划:爬楼梯](https://mp.weixin.qq.com/s/Ohop0jApSII9xxOMiFhGIw) 这道题目其实就是斐波那契数列。 +[动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html) 这道题目其实就是斐波那契数列。 但正常思考过程应该是推导完递推公式之后,发现这是斐波那契,而不是上来就知道这是斐波那契。 @@ -98,11 +98,11 @@ public: 这道绝佳的面试题我没有用过,如果录友们有面试别人的需求,就把这个套路拿去吧,哈哈哈。 -我在[通过一道面试题目,讲一讲递归算法的时间复杂度!](https://mp.weixin.qq.com/s/I6ZXFbw09NR31F5CJR_geQ)中,以我自己面试别人的真实经历,通过求x的n次方 这么简单的题目,就可以考察候选人对算法性能以及递归的理解深度,录友们可以看看,绝对有收获! +我在[通过一道面试题目,讲一讲递归算法的时间复杂度!](https://programmercarl.com/前序/通过一道面试题目,讲一讲递归算法的时间复杂度!.html)中,以我自己面试别人的真实经历,通过求x的n次方 这么简单的题目,就可以考察候选人对算法性能以及递归的理解深度,录友们可以看看,绝对有收获! ## 周四 -这道题目[动态规划:使用最小花费爬楼梯](https://mp.weixin.qq.com/s/djZB9gkyLFAKcQcSvKDorA)就是在爬台阶的基础上加了一个花费, +这道题目[动态规划:使用最小花费爬楼梯](https://programmercarl.com/0746.使用最小花费爬楼梯.html)就是在爬台阶的基础上加了一个花费, 这道题描述也确实有点魔幻。 diff --git a/problems/周总结/20210114动规周末总结.md b/problems/周总结/20210114动规周末总结.md index a49e0370..039f3596 100644 --- a/problems/周总结/20210114动规周末总结.md +++ b/problems/周总结/20210114动规周末总结.md @@ -1,7 +1,7 @@ ## 周一 -[动态规划:不同路径](https://mp.weixin.qq.com/s/MGgGIt4QCpFMROE9X9he_A)中求从出发点到终点有几种路径,只能向下或者向右移动一步。 +[动态规划:不同路径](https://programmercarl.com/0062.不同路径.html)中求从出发点到终点有几种路径,只能向下或者向右移动一步。 我们提供了三种方法,但重点讲解的还是动规,也是需要重点掌握的。 @@ -35,7 +35,7 @@ for (int i = 1; i < m; i++) { ## 周二 -[动态规划:不同路径还不够,要有障碍!](https://mp.weixin.qq.com/s/lhqF0O4le9-wvalptOVOww)相对于[动态规划:不同路径](https://mp.weixin.qq.com/s/MGgGIt4QCpFMROE9X9he_A)添加了障碍。 +[动态规划:不同路径还不够,要有障碍!](https://programmercarl.com/0063.不同路径II.html)相对于[动态规划:不同路径](https://programmercarl.com/0062.不同路径.html)添加了障碍。 dp[i][j]定义依然是:表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。 @@ -78,7 +78,7 @@ for (int i = 1; i < m; i++) { ## 周三 -[动态规划:整数拆分,你要怎么拆?](https://mp.weixin.qq.com/s/cVbyHrsWH_Rfzlj-ESr01A)给出一个整数,问有多少种拆分的方法。 +[动态规划:整数拆分,你要怎么拆?](https://programmercarl.com/0343.整数拆分.html)给出一个整数,问有多少种拆分的方法。 这道题目就有点难度了,题目中dp我也给出了两种方法,但通过两种方法的比较可以看出,对dp数组定义的理解,以及dp数组初始化的重要性。 @@ -121,7 +121,7 @@ for (int i = 3; i <= n ; i++) { **或者也可以理解j是拆分i的第一个整数**。 -[动态规划:整数拆分,你要怎么拆?](https://mp.weixin.qq.com/s/cVbyHrsWH_Rfzlj-ESr01A)总结里,我也给出了递推公式dp[i] = max(dp[i], dp[i - j] * dp[j])这种写法。 +[动态规划:整数拆分,你要怎么拆?](https://programmercarl.com/0343.整数拆分.html)总结里,我也给出了递推公式dp[i] = max(dp[i], dp[i - j] * dp[j])这种写法。 对于这种写法,一位录友总结的很好,意思就是:如果递推公式是dp[i-j] * dp[j],这样就相当于强制把一个数至少拆分成四份。 @@ -129,7 +129,7 @@ dp[i-j]至少是两个数的乘积,dp[j]又至少是两个数的乘积,但 ## 周四 -[动态规划:不同的二叉搜索树](https://mp.weixin.qq.com/s/8VE8pDrGxTf8NEVYBDwONw)给出n个不同的节点求能组成多少个不同二叉搜索树。 +[动态规划:不同的二叉搜索树](https://programmercarl.com/0096.不同的二叉搜索树.html)给出n个不同的节点求能组成多少个不同二叉搜索树。 这道题目还是比较难的,想到用动态规划的方法就很不容易了! @@ -145,7 +145,7 @@ n为5时候的dp数组状态如图: ## 总结 -本周题目已经开始点难度了,特别是[动态规划:不同的二叉搜索树](https://mp.weixin.qq.com/s/8VE8pDrGxTf8NEVYBDwONw)这道题目,明显感觉阅读量很低,可能是因为确实有点难吧。 +本周题目已经开始点难度了,特别是[动态规划:不同的二叉搜索树](https://programmercarl.com/0096.不同的二叉搜索树.html)这道题目,明显感觉阅读量很低,可能是因为确实有点难吧。 我现在也陷入了纠结,题目一简单,就会有录友和我反馈说题目太简单了,题目一难,阅读量就特别低。 diff --git a/problems/周总结/20210121动规周末总结.md b/problems/周总结/20210121动规周末总结.md index bffe47fb..e9427142 100644 --- a/problems/周总结/20210121动规周末总结.md +++ b/problems/周总结/20210121动规周末总结.md @@ -7,7 +7,7 @@ ## 周一 -[动态规划:关于01背包问题,你该了解这些!](https://mp.weixin.qq.com/s/FwIiPPmR18_AJO5eiidT6w)中,我们开始介绍了背包问题。 +[动态规划:关于01背包问题,你该了解这些!](https://programmercarl.com/背包理论基础01背包-1.html)中,我们开始介绍了背包问题。 首先对于背包的所有问题中,01背包是最最基础的,其他背包也是在01背包的基础上稍作变化。 @@ -75,7 +75,7 @@ for(int i = 1; i < weight.size(); i++) { // 遍历物品 ## 周二 -[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA)中把01背包的一维dp数组(滚动数组)实现详细讲解了一遍。 +[动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html)中把01背包的一维dp数组(滚动数组)实现详细讲解了一遍。 分析一下和二维dp数组有什么区别,在初始化和遍历顺序上又有什么差异? @@ -125,7 +125,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品 ## 周三 -[动态规划:416. 分割等和子集](https://mp.weixin.qq.com/s/sYw3QtPPQ5HMZCJcT4EaLQ)中我们开始用01背包来解决问题。 +[动态规划:416. 分割等和子集](https://programmercarl.com/0416.分割等和子集.html)中我们开始用01背包来解决问题。 只有确定了如下四点,才能把01背包问题套到本题上来。 @@ -138,11 +138,11 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品 ## 周四 -[动态规划:1049. 最后一块石头的重量 II](https://mp.weixin.qq.com/s/WbwAo3jaUaNJjvhHgq0BGg)这道题目其实和[动态规划:416. 分割等和子集](https://mp.weixin.qq.com/s/sYw3QtPPQ5HMZCJcT4EaLQ)是非常像的。 +[动态规划:1049. 最后一块石头的重量 II](https://programmercarl.com/1049.最后一块石头的重量II.html)这道题目其实和[动态规划:416. 分割等和子集](https://programmercarl.com/0416.分割等和子集.html)是非常像的。 本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。 -[动态规划:416. 分割等和子集](https://mp.weixin.qq.com/s/sYw3QtPPQ5HMZCJcT4EaLQ)相当于是求背包是否正好装满,而本题是求背包最多能装多少。 +[动态规划:416. 分割等和子集](https://programmercarl.com/0416.分割等和子集.html)相当于是求背包是否正好装满,而本题是求背包最多能装多少。 这两道题目是对dp[target]的处理方式不同。这也考验的对dp[i]定义的理解。 diff --git a/problems/周总结/20210128动规周末总结.md b/problems/周总结/20210128动规周末总结.md index bea4cc60..06193a70 100644 --- a/problems/周总结/20210128动规周末总结.md +++ b/problems/周总结/20210128动规周末总结.md @@ -2,7 +2,7 @@ ## 周一 -[动态规划:目标和!](https://mp.weixin.qq.com/s/2pWmaohX75gwxvBENS-NCw)要求在数列之间加入+ 或者 -,使其和为S。 +[动态规划:目标和!](https://programmercarl.com/0494.目标和.html)要求在数列之间加入+ 或者 -,使其和为S。 所有数的总和为sum,假设加法的总和为x,那么可以推出x = (S + sum) / 2。 @@ -39,7 +39,7 @@ dp数组状态变化如下: ## 周二 -这道题目[动态规划:一和零!](https://mp.weixin.qq.com/s/x-u3Dsp76DlYqtCe0xEKJw)算有点难度。 +这道题目[动态规划:一和零!](https://programmercarl.com/0474.一和零.html)算有点难度。 **不少同学都以为是多重背包,其实这是一道标准的01背包**。 @@ -78,7 +78,7 @@ dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1); 此时01背包我们就讲完了,正式开始完全背包。 -在[动态规划:关于完全背包,你该了解这些!](https://mp.weixin.qq.com/s/akwyxlJ4TLvKcw26KB9uJw)中我们讲解了完全背包的理论基础。 +在[动态规划:关于完全背包,你该了解这些!](https://programmercarl.com/背包问题理论基础完全背包.html)中我们讲解了完全背包的理论基础。 其实完全背包和01背包区别就是完全背包的物品是无限数量。 @@ -100,7 +100,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品 **那么为什么要先遍历物品,在遍历背包呢?** (灵魂拷问) -其实对于纯完全背包,先遍历物品,再遍历背包 与 先遍历背包,再遍历物品都是可以的。我在文中[动态规划:关于完全背包,你该了解这些!](https://mp.weixin.qq.com/s/akwyxlJ4TLvKcw26KB9uJw)也给出了详细的解释。 +其实对于纯完全背包,先遍历物品,再遍历背包 与 先遍历背包,再遍历物品都是可以的。我在文中[动态规划:关于完全背包,你该了解这些!](https://programmercarl.com/背包问题理论基础完全背包.html)也给出了详细的解释。 这个细节是很多同学忽略掉的点,其实也不算细节了,**相信不少同学在写背包的时候,两层for循环的先后循序搞不清楚,靠感觉来的**。 @@ -110,7 +110,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品 ## 周四 -在[动态规划:给你一些零钱,你要怎么凑?](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ)中就是给你一堆零钱(零钱个数无限),为凑成amount的组合数有几种。 +在[动态规划:给你一些零钱,你要怎么凑?](https://programmercarl.com/0518.零钱兑换II.html)中就是给你一堆零钱(零钱个数无限),为凑成amount的组合数有几种。 **注意这里组合数和排列数的区别!** @@ -134,7 +134,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品 其实这是一种错觉,或者说对动规理解的不够深入! -我在动规专题开篇介绍[关于动态规划,你该了解这些!](https://mp.weixin.qq.com/s/ocZwfPlCWrJtVGACqFNAag)中就强调了 **递推公式仅仅是 动规五部曲里的一小部分, dp数组的定义、初始化、遍历顺序,哪一点没有搞透的话,即使知道递推公式,遇到稍稍难一点的动规题目立刻会感觉写不出来了**。 +我在动规专题开篇介绍[关于动态规划,你该了解这些!](https://programmercarl.com/动态规划理论基础.html)中就强调了 **递推公式仅仅是 动规五部曲里的一小部分, dp数组的定义、初始化、遍历顺序,哪一点没有搞透的话,即使知道递推公式,遇到稍稍难一点的动规题目立刻会感觉写不出来了**。 此时相信大家对动规五部曲也有更深的理解了,同样也验证了Carl之前讲过的:**简单题是用来学习方法论的,而遇到难题才体现出方法论的重要性!** diff --git a/problems/周总结/20210204动规周末总结.md b/problems/周总结/20210204动规周末总结.md index d2417f8f..31009298 100644 --- a/problems/周总结/20210204动规周末总结.md +++ b/problems/周总结/20210204动规周末总结.md @@ -2,7 +2,7 @@ ## 周一 -[动态规划:377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)中给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数(顺序不同的序列被视作不同的组合)。 +[动态规划:377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)中给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数(顺序不同的序列被视作不同的组合)。 题目面试虽然是组合,但又强调顺序不同的序列被视作不同的组合,其实这道题目求的是排列数! @@ -10,7 +10,7 @@ 这个和前上周讲的组合问题又不一样,关键就体现在遍历顺序上! -在[动态规划:518.零钱兑换II](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ) 中就已经讲过了。 +在[动态规划:518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html) 中就已经讲过了。 **如果求组合数就是外层for循环遍历物品,内层for遍历背包**。 @@ -40,7 +40,7 @@ public: ## 周二 -爬楼梯之前我们已经做过了,就是斐波那契数列,很好解,但[动态规划:70. 爬楼梯进阶版(完全背包)](https://mp.weixin.qq.com/s/e_wacnELo-2PG76EjrUakA)中我们进阶了一下。 +爬楼梯之前我们已经做过了,就是斐波那契数列,很好解,但[动态规划:70. 爬楼梯进阶版(完全背包)](https://programmercarl.com/0070.爬楼梯完全背包版本.html)中我们进阶了一下。 改为:每次可以爬 1 、 2、.....、m 个台阶。问有多少种不同的方法可以爬到楼顶呢? @@ -53,7 +53,7 @@ public: **此时大家应该发现这就是一个完全背包问题了!** -和昨天的题目[动态规划:377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)基本就是一道题了,遍历顺序也是一样一样的! +和昨天的题目[动态规划:377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)基本就是一道题了,遍历顺序也是一样一样的! 代码如下: ```CPP @@ -77,7 +77,7 @@ public: ## 周三 -[动态规划:322.零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数(每种硬币的数量是无限的)。 +[动态规划:322.零钱兑换](https://programmercarl.com/0322.零钱兑换.html)给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数(每种硬币的数量是无限的)。 这里我们都知道这是完全背包。 @@ -137,10 +137,10 @@ public: ## 周四 -[动态规划:279.完全平方数](https://mp.weixin.qq.com/s/VfJT78p7UGpDZsapKF_QJQ)给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少(平方数可以重复使用)。 +[动态规划:279.完全平方数](https://programmercarl.com/0279.完全平方数.html)给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少(平方数可以重复使用)。 -如果按顺序把前面的文章都看了,这道题目就是简单题了。 dp[i]的定义,递推公式,初始化,遍历顺序,都是和[动态规划:322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ) 一样一样的。 +如果按顺序把前面的文章都看了,这道题目就是简单题了。 dp[i]的定义,递推公式,初始化,遍历顺序,都是和[动态规划:322. 零钱兑换](https://programmercarl.com/0322.零钱兑换.html) 一样一样的。 要是没有前面的基础上来做这道题,那这道题目就有点难度了。 @@ -193,9 +193,9 @@ public: 我这里做一下总结: -求组合数:[动态规划:518.零钱兑换II](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ) -求排列数:[动态规划:377. 组合总和 Ⅳ](https://mp.weixin.qq.com/s/Iixw0nahJWQgbqVNk8k6gA)、[动态规划:70. 爬楼梯进阶版(完全背包)](https://mp.weixin.qq.com/s/e_wacnELo-2PG76EjrUakA) -求最小数:[动态规划:322. 零钱兑换](https://mp.weixin.qq.com/s/dyk-xNilHzNtVdPPLObSeQ)、[动态规划:279.完全平方数](https://mp.weixin.qq.com/s/VfJT78p7UGpDZsapKF_QJQ) +求组合数:[动态规划:518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html) +求排列数:[动态规划:377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)、[动态规划:70. 爬楼梯进阶版(完全背包)](https://programmercarl.com/0070.爬楼梯完全背包版本.html) +求最小数:[动态规划:322. 零钱兑换](https://programmercarl.com/0322.零钱兑换.html)、[动态规划:279.完全平方数](https://programmercarl.com/0279.完全平方数.html) 此时我们就已经把完全背包的遍历顺序研究的透透的了! diff --git a/problems/周总结/20210225动规周末总结.md b/problems/周总结/20210225动规周末总结.md index ea3c5515..ae8b4800 100644 --- a/problems/周总结/20210225动规周末总结.md +++ b/problems/周总结/20210225动规周末总结.md @@ -3,7 +3,7 @@ ## 周一 -[动态规划:开始打家劫舍!](https://mp.weixin.qq.com/s/UZ31WdLEEFmBegdgLkJ8Dw)中就是给一个数组相邻之间不能连着偷,如果偷才能得到最大金钱。 +[动态规划:开始打家劫舍!](https://programmercarl.com/0198.打家劫舍.html)中就是给一个数组相邻之间不能连着偷,如果偷才能得到最大金钱。 1. 确定dp数组含义 @@ -35,7 +35,7 @@ dp[1] = max(nums[0], nums[1]); ## 周二 -[动态规划:继续打家劫舍!](https://mp.weixin.qq.com/s/kKPx4HpH3RArbRcxAVHbeQ)就是数组成环了,然后相邻的不能连着偷。 +[动态规划:继续打家劫舍!](https://programmercarl.com/0213.打家劫舍II.html)就是数组成环了,然后相邻的不能连着偷。 这里主要考虑清楚三种情况: @@ -61,11 +61,11 @@ dp[1] = max(nums[0], nums[1]); 所以我在本文重点强调了情况一二三是“考虑”的范围,而具体房间偷与不偷交给递推公式去抉择。 -剩下的就和[动态规划:开始打家劫舍!](https://mp.weixin.qq.com/s/UZ31WdLEEFmBegdgLkJ8Dw)是一个逻辑了。 +剩下的就和[动态规划:开始打家劫舍!](https://programmercarl.com/0198.打家劫舍.html)是一个逻辑了。 ## 周三 -[动态规划:还要打家劫舍!](https://mp.weixin.qq.com/s/BOJ1lHsxbQxUZffXlgglEQ)这次是在一颗二叉树上打家劫舍了,条件还是一样的,相临的不能偷。 +[动态规划:还要打家劫舍!](https://programmercarl.com/0337.打家劫舍III.html)这次是在一颗二叉树上打家劫舍了,条件还是一样的,相临的不能偷。 这道题目是树形DP的入门题目,其实树形DP其实就是在树上进行递推公式的推导,没有什么神秘的。 @@ -184,14 +184,14 @@ return {val2, val1}; 因为平时我们习惯了在一维数组或者二维数组上推导公式,一下子换成了树,就需要对树的遍历方式足够了解! -大家还记不记得我在讲解贪心专题的时候,讲到这道题目:[贪心算法:我要监控二叉树!](https://mp.weixin.qq.com/s/kCxlLLjWKaE6nifHC3UL2Q),这也是贪心算法在树上的应用。**那我也可以把这个算法起一个名字,叫做树形贪心**,哈哈哈 +大家还记不记得我在讲解贪心专题的时候,讲到这道题目:[贪心算法:我要监控二叉树!](https://programmercarl.com/0968.监控二叉树.html),这也是贪心算法在树上的应用。**那我也可以把这个算法起一个名字,叫做树形贪心**,哈哈哈 “树形贪心”词汇从此诞生,来自「代码随想录」 ## 周四 -[动态规划:买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ) 一段时间,只能买买一次,问最大收益。 +[动态规划:买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html) 一段时间,只能买买一次,问最大收益。 这里我给出了三中解法: diff --git a/problems/周总结/20210304动规周末总结.md b/problems/周总结/20210304动规周末总结.md index 9fea6244..dad9884d 100644 --- a/problems/周总结/20210304动规周末总结.md +++ b/problems/周总结/20210304动规周末总结.md @@ -3,9 +3,9 @@ ## 周一 -[动态规划:买卖股票的最佳时机II](https://mp.weixin.qq.com/s/d4TRWFuhaY83HPa6t5ZL-w)中股票可以买买多了次! +[动态规划:买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II(动态规划).html)中股票可以买买多了次! -这也是和[121. 买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ)的唯一区别(注意只有一只股票,所以再次购买前要出售掉之前的股票) +这也是和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的唯一区别(注意只有一只股票,所以再次购买前要出售掉之前的股票) 重点在于递推公式公式的不同。 @@ -22,7 +22,7 @@ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]); ``` -大家可以发现本题和[121. 买卖股票的最佳时机](https://mp.weixin.qq.com/s/keWo5qYJY4zmHn3amfXdfQ)的代码几乎一样,唯一的区别在: +大家可以发现本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的代码几乎一样,唯一的区别在: ``` dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); @@ -32,7 +32,7 @@ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); ## 周二 -[动态规划:买卖股票的最佳时机III](https://mp.weixin.qq.com/s/Sbs157mlVDtAR0gbLpdKzg)中最多只能完成两笔交易。 +[动态规划:买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html)中最多只能完成两笔交易。 **这意味着可以买卖一次,可以买卖两次,也可以不买卖**。 @@ -85,9 +85,9 @@ dp[0][4] = 0; ## 周三 -[动态规划:买卖股票的最佳时机IV](https://mp.weixin.qq.com/s/jtxZJWAo2y5sUsW647Z5cw)最多可以完成 k 笔交易。 +[动态规划:买卖股票的最佳时机IV](https://programmercarl.com/0188.买卖股票的最佳时机IV.html)最多可以完成 k 笔交易。 -相对于上一道[动态规划:123.买卖股票的最佳时机III](https://mp.weixin.qq.com/s/Sbs157mlVDtAR0gbLpdKzg),本题需要通过前两次的交易,来类比前k次的交易 +相对于上一道[动态规划:123.买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html),本题需要通过前两次的交易,来类比前k次的交易 1. 确定dp数组以及下标的含义 @@ -117,7 +117,7 @@ for (int j = 0; j < 2 * k - 1; j += 2) { } ``` -**本题和[动态规划:123.买卖股票的最佳时机III](https://mp.weixin.qq.com/s/Sbs157mlVDtAR0gbLpdKzg)最大的区别就是这里要类比j为奇数是买,偶数是卖剩的状态**。 +**本题和[动态规划:123.买卖股票的最佳时机III](https://programmercarl.com/0123.买卖股票的最佳时机III.html)最大的区别就是这里要类比j为奇数是买,偶数是卖剩的状态**。 3. dp数组如何初始化 @@ -147,9 +147,9 @@ for (int j = 1; j < 2 * k; j += 2) { ## 周四 -[动态规划:最佳买卖股票时机含冷冻期](https://mp.weixin.qq.com/s/IgC0iWWCDpYL9ZbTHGHgfw)尽可能地完成更多的交易(多次买卖一支股票),但有冷冻期,冷冻期为1天 +[动态规划:最佳买卖股票时机含冷冻期](https://programmercarl.com/0309.最佳买卖股票时机含冷冻期.html)尽可能地完成更多的交易(多次买卖一支股票),但有冷冻期,冷冻期为1天 -相对于[动态规划:122.买卖股票的最佳时机II](https://mp.weixin.qq.com/s/d4TRWFuhaY83HPa6t5ZL-w),本题加上了一个冷冻期 +相对于[动态规划:122.买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II(动态规划).html),本题加上了一个冷冻期 **本题则需要第三个状态:不持有股票(冷冻期)的最多现金**。 diff --git a/problems/算法模板.md b/problems/算法模板.md index 8f6b053e..b56678ab 100644 --- a/problems/算法模板.md +++ b/problems/算法模板.md @@ -174,7 +174,7 @@ vector postorderTraversal(TreeNode* root) { ``` ### 广度优先遍历(队列) -相关题解:[0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md) +相关题解:[0102.二叉树的层序遍历](https://programmercarl.com/0102.二叉树的层序遍历.html) ``` vector> levelOrder(TreeNode* root) { @@ -202,13 +202,13 @@ vector> levelOrder(TreeNode* root) { 可以直接解决如下题目: -* [0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md) +* [0102.二叉树的层序遍历](https://programmercarl.com/0102.二叉树的层序遍历.html) * [0199.二叉树的右视图](https://github.com/youngyangyang04/leetcode/blob/master/problems/0199.二叉树的右视图.md) * [0637.二叉树的层平均值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0637.二叉树的层平均值.md) -* [0104.二叉树的最大深度 (迭代法)](https://github.com/youngyangyang04/leetcode/blob/master/problems/0104.二叉树的最大深度.md) +* [0104.二叉树的最大深度 (迭代法)](https://programmercarl.com/0104.二叉树的最大深度.html) -* [0111.二叉树的最小深度(迭代法)]((https://github.com/youngyangyang04/leetcode/blob/master/problems/0111.二叉树的最小深度.md)) -* [0222.完全二叉树的节点个数(迭代法)](https://github.com/youngyangyang04/leetcode/blob/master/problems/0222.完全二叉树的节点个数.md) +* [0111.二叉树的最小深度(迭代法)](https://programmercarl.com/0111.二叉树的最小深度.html) +* [0222.完全二叉树的节点个数(迭代法)](https://programmercarl.com/0222.完全二叉树的节点个数.html) ### 二叉树深度 diff --git a/problems/背包理论基础01背包-1.md b/problems/背包理论基础01背包-1.md index 851c2657..2bcded70 100644 --- a/problems/背包理论基础01背包-1.md +++ b/problems/背包理论基础01背包-1.md @@ -137,7 +137,7 @@ dp[0][j] 和 dp[i][0] 都已经初始化了,那么其他下标应该初始化 ``` // 初始化 dp -vector> dp(weight.size() + 1, vector(bagWeight + 1, 0)); +vector> dp(weight.size(), vector(bagWeight + 1, 0)); for (int j = weight[0]; j <= bagWeight; j++) { dp[0][j] = value[0]; } @@ -230,7 +230,7 @@ void test_2_wei_bag_problem1() { int bagWeight = 4; // 二维数组 - vector> dp(weight.size() + 1, vector(bagWeight + 1, 0)); + vector> dp(weight.size(), vector(bagWeight + 1, 0)); // 初始化 for (int j = weight[0]; j <= bagWeight; j++) { diff --git a/problems/背包问题理论基础完全背包.md b/problems/背包问题理论基础完全背包.md index f4abc535..455a3c33 100644 --- a/problems/背包问题理论基础完全背包.md +++ b/problems/背包问题理论基础完全背包.md @@ -80,7 +80,7 @@ dp状态图如下: * [动态规划:关于01背包问题,你该了解这些!](https://programmercarl.com/背包理论基础01背包-1.html) * [动态规划:关于01背包问题,你该了解这些!(滚动数组)](https://programmercarl.com/背包理论基础01背包-2.html) -就知道了,01背包中二维dp数组的两个for遍历的先后循序是可以颠倒了,一位dp数组的两个for循环先后循序一定是先遍历物品,再遍历背包容量。 +就知道了,01背包中二维dp数组的两个for遍历的先后循序是可以颠倒了,一维dp数组的两个for循环先后循序一定是先遍历物品,再遍历背包容量。 **在完全背包中,对于一维dp数组来说,其实两个for循环嵌套顺序同样无所谓!**