From fddea62f38df395a6094f72f17a00fa831634821 Mon Sep 17 00:00:00 2001 From: fw_qaq <82551626+Jack-Zhang-1314@users.noreply.github.com> Date: Sat, 22 Oct 2022 16:42:48 +0800 Subject: [PATCH 01/12] update 15.3sum about rust --- problems/0015.三数之和.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/problems/0015.三数之和.md b/problems/0015.三数之和.md index fb289d54..5ee57127 100644 --- a/problems/0015.三数之和.md +++ b/problems/0015.三数之和.md @@ -655,7 +655,7 @@ impl Solution { ```Rust // 双指针法 -use std::collections::HashSet; +use std::cmp::Ordering; impl Solution { pub fn three_sum(nums: Vec) -> Vec> { let mut result: Vec> = Vec::new(); @@ -667,22 +667,21 @@ impl Solution { if i > 0 && nums[i] == nums[i - 1] { continue; } let (mut left, mut right) = (i + 1, len - 1); while left < right { - if nums[i] + nums[left] + nums[right] > 0 { - right -= 1; - // 去重 - while left < right && nums[right] == nums[right + 1] { right -= 1; } - } else if nums[i] + nums[left] + nums[right] < 0 { - left += 1; - // 去重 - while left < right && nums[left] == nums[left - 1] { left += 1; } - } else { - result.push(vec![nums[i], nums[left], nums[right]]); - // 去重 - right -= 1; - left += 1; - while left < right && nums[right] == nums[right + 1] { right -= 1; } - while left < right && nums[left] == nums[left - 1] { left += 1; } - } + match (nums[i] + nums[left] + nums[right]).cmp(&0){ + Ordering::Equal =>{ + result.push(vec![nums[i], nums[left], nums[right]]); + left +=1; + right -=1; + while left < right && nums[left] == nums[left - 1]{ + left += 1; + } + while left < right && nums[right] == nums[right+1]{ + right -= 1; + } + } + Ordering::Greater => right -= 1, + Ordering::Less => left += 1, + } } } result From 53793b70f9c9068bd45607d73465ed8256af30ac Mon Sep 17 00:00:00 2001 From: fw_qaq <82551626+Jack-Zhang-1314@users.noreply.github.com> Date: Sat, 22 Oct 2022 21:55:08 +0800 Subject: [PATCH 02/12] update 18.4sum about rust --- problems/0018.四数之和.md | 36 +++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/problems/0018.四数之和.md b/problems/0018.四数之和.md index 463eaf8f..ca33eb57 100644 --- a/problems/0018.四数之和.md +++ b/problems/0018.四数之和.md @@ -527,6 +527,7 @@ public class Solution Rust: ```Rust +use std::cmp::Ordering; impl Solution { pub fn four_sum(nums: Vec, target: i32) -> Vec> { let mut result: Vec> = Vec::new(); @@ -545,22 +546,25 @@ impl Solution { if i > k + 1 && nums[i] == nums[i - 1] { continue; } let (mut left, mut right) = (i + 1, len - 1); while left < right { - if nums[k] + nums[i] > target - (nums[left] + nums[right]) { - right -= 1; - // 去重 - while left < right && nums[right] == nums[right + 1] { right -= 1; } - } else if nums[k] + nums[i] < target - (nums[left] + nums[right]) { - left += 1; - // 去重 - while left < right && nums[left] == nums[left - 1] { left += 1; } - } else { - result.push(vec![nums[k], nums[i], nums[left], nums[right]]); - // 去重 - while left < right && nums[right] == nums[right - 1] { right -= 1; } - while left < right && nums[left] == nums[left + 1] { left += 1; } - left += 1; - right -= 1; - } + match (nums[k] + nums[i] + nums[left] + nums[right]).cmp(&target){ + Ordering::Equal => { + result.push(vec![nums[k], nums[i], nums[left], nums[right]]); + left += 1; + right -= 1; + while left < right && nums[left] == nums[left - 1]{ + left += 1; + } + while left < right && nums[right] == nums[right + 1]{ + right -= 1; + } + } + Ordering::Less => { + left +=1; + }, + Ordering::Greater => { + right -= 1; + } + } } } } From 8f7f6185db0ed11af2ceb447ec0f1c84664dc359 Mon Sep 17 00:00:00 2001 From: pwq <57176977+pvvq@users.noreply.github.com> Date: Tue, 25 Oct 2022 01:10:21 +0800 Subject: [PATCH 03/12] 0349 fix code block language change language from `c++` to `CPP`, since `c++` doesn't get rendered as expected (here)[https://programmercarl.com/0349.%E4%B8%A4%E4%B8%AA%E6%95%B0%E7%BB%84%E7%9A%84%E4%BA%A4%E9%9B%86.html#%E5%90%8E%E8%AE%B0]. --- problems/0349.两个数组的交集.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0349.两个数组的交集.md b/problems/0349.两个数组的交集.md index a6e07424..bd83fae9 100644 --- a/problems/0349.两个数组的交集.md +++ b/problems/0349.两个数组的交集.md @@ -91,7 +91,7 @@ public: 对应C++代码如下: -```c++ +```CPP class Solution { public: vector intersection(vector& nums1, vector& nums2) { From 5c3a57ea1c147502a769312374853955bbdd4dca Mon Sep 17 00:00:00 2001 From: c0dedance <38075730+c0dedance@users.noreply.github.com> Date: Tue, 25 Oct 2022 21:34:47 +0800 Subject: [PATCH 04/12] =?UTF-8?q?Update=200343.=E6=95=B4=E6=95=B0=E6=8B=86?= =?UTF-8?q?=E5=88=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 内层for循环优化 --- problems/0343.整数拆分.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/problems/0343.整数拆分.md b/problems/0343.整数拆分.md index 00e6a9da..f6683954 100644 --- a/problems/0343.整数拆分.md +++ b/problems/0343.整数拆分.md @@ -89,7 +89,7 @@ dp[i] 是依靠 dp[i - j]的状态,所以遍历i一定是从前向后遍历, 所以遍历顺序为: ``` for (int i = 3; i <= n ; i++) { - for (int j = 1; j < i - 1; j++) { + for (int j = 1; j <= i / 2; j++) { dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j)); } } @@ -110,7 +110,7 @@ public: vector dp(n + 1); dp[2] = 1; for (int i = 3; i <= n ; i++) { - for (int j = 1; j < i - 1; j++) { + for (int j = 1; j <= i / 2; j++) { dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j)); } } @@ -167,7 +167,7 @@ public: dp[2] = 2; dp[3] = 3; for (int i = 4; i <= n ; i++) { - for (int j = 1; j < i - 1; j++) { + for (int j = 1; j <= i / 2; j++) { dp[i] = max(dp[i], dp[i - j] * dp[j]); } } @@ -224,7 +224,7 @@ class Solution: # 假设对正整数 i 拆分出的第一个正整数是 j(1 <= j < i),则有以下两种方案: # 1) 将 i 拆分成 j 和 i−j 的和,且 i−j 不再拆分成多个正整数,此时的乘积是 j * (i-j) # 2) 将 i 拆分成 j 和 i−j 的和,且 i−j 继续拆分成多个正整数,此时的乘积是 j * dp[i-j] - for j in range(1, i - 1): + for j in range(1, i / 2 + 1): dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j])) return dp[n] ``` @@ -281,7 +281,7 @@ var integerBreak = function(n) { dp[2] = 1 for(let i = 3; i <= n; i++) { - for(let j = 1; j < i; j++) { + for(let j = 1; j <= i / 2; j++) { dp[i] = Math.max(dp[i], dp[i - j] * j, (i - j) * j) } } @@ -306,7 +306,7 @@ function integerBreak(n: number): number { const dp: number[] = new Array(n + 1).fill(0); dp[2] = 1; for (let i = 3; i <= n; i++) { - for (let j = 1; j <= i - 2; j++) { + for (let j = 1; j <= i / 2; j++) { dp[i] = Math.max(dp[i], j * dp[i - j], j * (i - j)); } } From 84b4f2674436910c87e258b5984b1404591bc060 Mon Sep 17 00:00:00 2001 From: fw_qaq <82551626+Jack-Zhang-1314@users.noreply.github.com> Date: Tue, 25 Oct 2022 22:17:21 +0800 Subject: [PATCH 05/12] =?UTF-8?q?update=2028.=E6=89=BE=E5=87=BA=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2=E4=B8=AD=E7=AC=AC=E4=B8=80=E4=B8=AA=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=E9=A1=B9=E7=9A=84=E4=B8=8B=E6=A0=87=20about?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0028.实现strStr.md | 47 +++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/problems/0028.实现strStr.md b/problems/0028.实现strStr.md index 7078738e..697e8c2d 100644 --- a/problems/0028.实现strStr.md +++ b/problems/0028.实现strStr.md @@ -1299,6 +1299,53 @@ impl Solution { } ``` +> 前缀表统一减一 + +```rust +impl Solution { + pub fn get_next(mut next: Vec, s: &Vec) -> Vec { + let mut j = -1; + for i in 1..s.len() { + while j >= 0 && s[(j + 1) as usize] != s[i] { + j = next[j as usize]; + } + if s[i] == s[(j + 1) as usize] { + j += 1; + } + next[i] = j; + } + next + } + pub fn str_str(haystack: String, needle: String) -> i32 { + if needle.is_empty() { + return 0; + } + if haystack.len() < needle.len() { + return -1; + } + let (haystack_chars, needle_chars) = ( + haystack.chars().collect::>(), + needle.chars().collect::>(), + ); + let mut j = -1; + let mut next = vec![-1; needle.len()]; + next = Self::get_next(next, &needle_chars); + for (i, v) in haystack_chars.into_iter().enumerate() { + while j >= 0 && v != needle_chars[(j + 1) as usize] { + j = next[j as usize]; + } + if v == needle_chars[(j + 1) as usize] { + j += 1; + } + if j == needle_chars.len() as i32 - 1 { + return (i - needle_chars.len() + 1) as i32; + } + } + -1 + } +} +``` +

From a582640c7a800cbce116f1cd6475a0a68a629ff3 Mon Sep 17 00:00:00 2001 From: fw_qaq <82551626+Jack-Zhang-1314@users.noreply.github.com> Date: Tue, 25 Oct 2022 22:35:54 +0800 Subject: [PATCH 06/12] =?UTF-8?q?Update=200028.=E5=AE=9E=E7=8E=B0strStr.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0028.实现strStr.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/problems/0028.实现strStr.md b/problems/0028.实现strStr.md index 697e8c2d..19d16e9f 100644 --- a/problems/0028.实现strStr.md +++ b/problems/0028.实现strStr.md @@ -1303,7 +1303,8 @@ impl Solution { ```rust impl Solution { - pub fn get_next(mut next: Vec, s: &Vec) -> Vec { + pub fn get_next(next_len: usize, s: &Vec) -> Vec { + let mut next = vec![-1; next_len]; let mut j = -1; for i in 1..s.len() { while j >= 0 && s[(j + 1) as usize] != s[i] { @@ -1328,8 +1329,7 @@ impl Solution { needle.chars().collect::>(), ); let mut j = -1; - let mut next = vec![-1; needle.len()]; - next = Self::get_next(next, &needle_chars); + let next = Self::get_next(needle.len(), &needle_chars); for (i, v) in haystack_chars.into_iter().enumerate() { while j >= 0 && v != needle_chars[(j + 1) as usize] { j = next[j as usize]; From 856907e3d596f6796b4817aa6b80a26e6dbb73d6 Mon Sep 17 00:00:00 2001 From: fw_qaq <82551626+Jack-Zhang-1314@users.noreply.github.com> Date: Tue, 25 Oct 2022 23:26:04 +0800 Subject: [PATCH 07/12] =?UTF-8?q?update=200459.=E9=87=8D=E5=A4=8D=E7=9A=84?= =?UTF-8?q?=E5=AD=90=E5=AD=97=E7=AC=A6=E4=B8=B2.md=20about=20rust=20?= =?UTF-8?q?=E5=89=8D=E7=BC=80=E8=A1=A8=E7=BB=9F=E4=B8=80=20-1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0459.重复的子字符串.md | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/problems/0459.重复的子字符串.md b/problems/0459.重复的子字符串.md index 18de79d5..378d1a63 100644 --- a/problems/0459.重复的子字符串.md +++ b/problems/0459.重复的子字符串.md @@ -615,6 +615,35 @@ impl Solution { } ``` +>前缀表统一减一 + +```rust + pub fn get_next(next_len: usize, s: &Vec) -> Vec { + let mut next = vec![-1; next_len]; + let mut j = -1; + for i in 1..s.len() { + while j >= 0 && s[i] != s[(j + 1) as usize] { + j = next[j as usize]; + } + if s[i] == s[(j + 1) as usize] { + j += 1; + } + next[i] = j; + } + next + } + pub fn repeated_substring_pattern(s: String) -> bool { + let s_chars = s.chars().collect::>(); + let next = Self::get_next(s_chars.len(), &s_chars); + if next[s_chars.len() - 1] >= 0 + && s_chars.len() % (s_chars.len() - (next[s_chars.len() - 1] + 1) as usize) == 0 + { + return true; + } + false + } +``` +

From f386e3ca68c602c84f875ab1b6538a41e000459b Mon Sep 17 00:00:00 2001 From: fw_qaq <82551626+Jack-Zhang-1314@users.noreply.github.com> Date: Tue, 25 Oct 2022 23:27:09 +0800 Subject: [PATCH 08/12] =?UTF-8?q?Update=200459.=E9=87=8D=E5=A4=8D=E7=9A=84?= =?UTF-8?q?=E5=AD=90=E5=AD=97=E7=AC=A6=E4=B8=B2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0459.重复的子字符串.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/problems/0459.重复的子字符串.md b/problems/0459.重复的子字符串.md index 378d1a63..b7d86880 100644 --- a/problems/0459.重复的子字符串.md +++ b/problems/0459.重复的子字符串.md @@ -618,6 +618,7 @@ impl Solution { >前缀表统一减一 ```rust +impl Solution { pub fn get_next(next_len: usize, s: &Vec) -> Vec { let mut next = vec![-1; next_len]; let mut j = -1; @@ -642,6 +643,7 @@ impl Solution { } false } +} ``` From 1204f0e4ed06d11d5e55ad47d94fc71733bf7ce5 Mon Sep 17 00:00:00 2001 From: Liu YongLiang <41845017+tlylt@users.noreply.github.com> Date: Wed, 26 Oct 2022 20:30:32 +0800 Subject: [PATCH 09/12] =?UTF-8?q?Update=201221.=E5=88=86=E5=89=B2=E5=B9=B3?= =?UTF-8?q?=E8=A1=A1=E5=AD=97=E7=AC=A6=E4=B8=B2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/1221.分割平衡字符串.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/1221.分割平衡字符串.md b/problems/1221.分割平衡字符串.md index 94d7bca8..b587514a 100644 --- a/problems/1221.分割平衡字符串.md +++ b/problems/1221.分割平衡字符串.md @@ -117,7 +117,7 @@ class Solution: diff -= 1 else: diff += 1 - if tilt == 0: + if diff == 0: ans += 1 return ans ``` From 448ef5f40618d2f7428e6ea7107f2f9f24fe987c Mon Sep 17 00:00:00 2001 From: xiaoyu2018 <861900161@qq.com> Date: Fri, 28 Oct 2022 10:57:36 +0800 Subject: [PATCH 10/12] update 0121 and 0122 --- problems/0121.买卖股票的最佳时机.md | 42 +++++++++++++++++++ ...票的最佳时机II(动态规划).md | 42 +++++++++++++++++-- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/problems/0121.买卖股票的最佳时机.md b/problems/0121.买卖股票的最佳时机.md index 790bbd88..63ac5d04 100644 --- a/problems/0121.买卖股票的最佳时机.md +++ b/problems/0121.买卖股票的最佳时机.md @@ -464,6 +464,47 @@ function maxProfit(prices: number[]): number { }; ``` +C#: + +> 贪心法 + +```csharp +public class Solution +{ + public int MaxProfit(int[] prices) + { + int min = Int32.MaxValue; + int res = 0; + for (int i = 0; i < prices.Length; i++) + { + min = Math.Min(prices[i], min); + res = Math.Max(prices[i] - min, res); + } + return res; + } +} +``` + +> 动态规划 + +```csharp +public class Solution +{ + public int MaxProfit(int[] prices) + { + int[] dp = new int[2]; + int size = prices.Length; + (dp[0], dp[1]) = (-prices[0], 0); + for (int i = 0; i < size; i++) + { + dp[0] = Math.Max(dp[0], -prices[i]); + dp[1] = Math.Max(dp[1], dp[0]+prices[i]); + } + return dp[1]; + } +} +``` + @@ -471,3 +512,4 @@ function maxProfit(prices: number[]): number { + diff --git a/problems/0122.买卖股票的最佳时机II(动态规划).md b/problems/0122.买卖股票的最佳时机II(动态规划).md index f5f3f720..f2aec68b 100644 --- a/problems/0122.买卖股票的最佳时机II(动态规划).md +++ b/problems/0122.买卖股票的最佳时机II(动态规划).md @@ -169,8 +169,6 @@ class Solution { } ``` - - Python: > 版本一: @@ -253,8 +251,6 @@ func max(a,b int)int{ } ``` - - Javascript: ```javascript // 方法一:动态规划(dp 数组) @@ -331,9 +327,47 @@ function maxProfit(prices: number[]): number { }; ``` +C#: + +> 贪心法 + +```csharp +public class Solution +{ + public int MaxProfit(int[] prices) + { + int res = 0; + for (int i = 1; i < prices.Length; i++) + res += Math.Max(0, prices[i] - prices[i-1]); + return res; + } +} +``` + +> 动态规划 + +```csharp +public class Solution +{ + public int MaxProfit(int[] prices) + { + int[] dp = new int[2]; + dp[0] = -prices[0]; + + for (int i = 1; i < prices.Length; i++) + { + dp[0] = dp[0]>dp[1] - prices[i]?dp[0]:dp[1] - prices[i]; + dp[1] = dp[1] > dp[0] + prices[i] ? dp[1] : dp[0] + prices[i]; + } + return dp[1]; + } +} +``` +

+ From 924f5b105d8ba42cdef159570356eb40deb4a375 Mon Sep 17 00:00:00 2001 From: Peilin Sun <46664832+PPPerry@users.noreply.github.com> Date: Fri, 28 Oct 2022 20:53:21 +0800 Subject: [PATCH 11/12] =?UTF-8?q?Update=20=E6=A0=88=E4=B8=8E=E9=98=9F?= =?UTF-8?q?=E5=88=97=E7=90=86=E8=AE=BA=E5=9F=BA=E7=A1=80.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 错别字 --- problems/栈与队列理论基础.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/栈与队列理论基础.md b/problems/栈与队列理论基础.md index da3a6dfa..66b86919 100644 --- a/problems/栈与队列理论基础.md +++ b/problems/栈与队列理论基础.md @@ -59,7 +59,7 @@ C++标准库是有多个版本的,要知道我们使用的STL是哪个版本 ![栈与队列理论3](https://img-blog.csdnimg.cn/20210104235459376.png) -**我们常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的低层结构。** +**我们常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的底层结构。** deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。 From f29352fa2760bd04bf6b4dcf53a626d92bd2833f Mon Sep 17 00:00:00 2001 From: programmercarl <826123027@qq.com> Date: Fri, 4 Nov 2022 12:05:26 +0800 Subject: [PATCH 12/12] Update --- problems/0037.解数独.md | 22 ++- problems/0040.组合总和II.md | 8 + problems/0047.全排列II.md | 2 +- problems/0062.不同路径.md | 2 +- problems/0070.爬楼梯.md | 4 + problems/0070.爬楼梯完全背包版本.md | 11 +- problems/0101.对称二叉树.md | 2 +- problems/0102.二叉树的层序遍历.md | 20 -- problems/0150.逆波兰表达式求值.md | 4 - problems/0242.有效的字母异位词.md | 2 +- problems/0279.完全平方数.md | 4 +- problems/0322.零钱兑换.md | 3 +- problems/0509.斐波那契数.md | 3 + problems/0746.使用最小花费爬楼梯.md | 190 +++++++++---------- problems/前序/vim.md | 7 +- problems/贪心算法总结篇.md | 3 + 16 files changed, 142 insertions(+), 145 deletions(-) diff --git a/problems/0037.解数独.md b/problems/0037.解数独.md index f1f621ed..fc1e3114 100644 --- a/problems/0037.解数独.md +++ b/problems/0037.解数独.md @@ -44,7 +44,7 @@ [N皇后问题](https://programmercarl.com/0051.N皇后.html)是因为每一行每一列只放一个皇后,只需要一层for循环遍历一行,递归来来遍历列,然后一行一列确定皇后的唯一位置。 -本题就不一样了,**本题中棋盘的每一个位置都要放一个数字,并检查数字是否合法,解数独的树形结构要比N皇后更宽更深**。 +本题就不一样了,**本题中棋盘的每一个位置都要放一个数字(而N换后是一行只放一个皇后),并检查数字是否合法,解数独的树形结构要比N皇后更宽更深**。 因为这个树形结构太大了,我抽取一部分,如图所示: @@ -57,7 +57,7 @@ **递归函数的返回值需要是bool类型,为什么呢?** -因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值,这一点在[回溯算法:N皇后问题](https://programmercarl.com/0051.N皇后.html)中已经介绍过了,一样的道理。 +因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值。 代码如下: @@ -157,15 +157,16 @@ private: bool backtracking(vector>& board) { for (int i = 0; i < board.size(); i++) { // 遍历行 for (int j = 0; j < board[0].size(); j++) { // 遍历列 - if (board[i][j] != '.') continue; - for (char k = '1'; k <= '9'; k++) { // (i, j) 这个位置放k是否合适 - if (isValid(i, j, k, board)) { - board[i][j] = k; // 放置k - if (backtracking(board)) return true; // 如果找到合适一组立刻返回 - board[i][j] = '.'; // 回溯,撤销k + if (board[i][j] == '.') { + for (char k = '1'; k <= '9'; k++) { // (i, j) 这个位置放k是否合适 + if (isValid(i, j, k, board)) { + board[i][j] = k; // 放置k + if (backtracking(board)) return true; // 如果找到合适一组立刻返回 + board[i][j] = '.'; // 回溯,撤销k + } } - } - return false; // 9个数都试完了,都不行,那么就返回false + return false; // 9个数都试完了,都不行,那么就返回false + } } } return true; // 遍历完没有返回false,说明找到了合适棋盘位置了 @@ -197,6 +198,7 @@ public: backtracking(board); } }; + ``` ## 总结 diff --git a/problems/0040.组合总和II.md b/problems/0040.组合总和II.md index 46b33c89..fcfc396b 100644 --- a/problems/0040.组合总和II.md +++ b/problems/0040.组合总和II.md @@ -131,8 +131,16 @@ if (sum == target) { * used[i - 1] == true,说明同一树枝candidates[i - 1]使用过 * used[i - 1] == false,说明同一树层candidates[i - 1]使用过 +可能有的录友想,为什么 used[i - 1] == false 就是同一树层呢,因为同一树层,used[i - 1] == false 才能表示,当前取的 candidates[i] 是从 candidates[i - 1] 回溯而来的。 + +而 used[i - 1] == true,说明是进入下一层递归,去下一个数,所以是树枝上,如图所示: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20221021163812.png) + + **这块去重的逻辑很抽象,网上搜的题解基本没有能讲清楚的,如果大家之前思考过这个问题或者刷过这道题目,看到这里一定会感觉通透了很多!** + 那么单层搜索的逻辑代码如下: ```CPP diff --git a/problems/0047.全排列II.md b/problems/0047.全排列II.md index bac30a44..c809c62d 100644 --- a/problems/0047.全排列II.md +++ b/problems/0047.全排列II.md @@ -66,7 +66,7 @@ private: } for (int i = 0; i < nums.size(); i++) { // used[i - 1] == true,说明同一树枝nums[i - 1]使用过 - // used[i - 1] == false,说明同一树层nums[i - 1]使用过 + // used[i - 1] == false,说明同一树层nums[i - 1]使用过 // 如果同一树层nums[i - 1]使用过则直接跳过 if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) { continue; diff --git a/problems/0062.不同路径.md b/problems/0062.不同路径.md index ad5c6f3e..2367587c 100644 --- a/problems/0062.不同路径.md +++ b/problems/0062.不同路径.md @@ -114,7 +114,7 @@ for (int j = 0; j < n; j++) dp[0][j] = 1; 4. 确定遍历顺序 -这里要看一下递归公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1],dp[i][j]都是从其上方和左方推导而来,那么从左到右一层一层遍历就可以了。 +这里要看一下递推公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1],dp[i][j]都是从其上方和左方推导而来,那么从左到右一层一层遍历就可以了。 这样就可以保证推导dp[i][j]的时候,dp[i - 1][j] 和 dp[i][j - 1]一定是有数值的。 diff --git a/problems/0070.爬楼梯.md b/problems/0070.爬楼梯.md index 3acc4c11..335ddb23 100644 --- a/problems/0070.爬楼梯.md +++ b/problems/0070.爬楼梯.md @@ -28,6 +28,10 @@ * 1 阶 + 2 阶 * 2 阶 + 1 阶 +# 视频讲解 + +**《代码随想录》算法视频公开课:[带你学透动态规划-爬楼梯|LeetCode:70.爬楼梯)](https://www.bilibili.com/video/BV17h411h7UH),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 + ## 思路 diff --git a/problems/0070.爬楼梯完全背包版本.md b/problems/0070.爬楼梯完全背包版本.md index 4925c312..3093c833 100644 --- a/problems/0070.爬楼梯完全背包版本.md +++ b/problems/0070.爬楼梯完全背包版本.md @@ -3,13 +3,8 @@

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

-# 动态规划:以前我没得选,现在我选择再爬一次! -之前讲这道题目的时候,因为还没有讲背包问题,所以就只是讲了一下爬楼梯最直接的动规方法(斐波那契)。 - -**这次终于讲到了背包问题,我选择带录友们再爬一次楼梯!** - -## 70. 爬楼梯 +# 70. 爬楼梯 [力扣题目链接](https://leetcode.cn/problems/climbing-stairs/) @@ -36,6 +31,10 @@ ## 思路 +之前讲这道题目的时候,因为还没有讲背包问题,所以就只是讲了一下爬楼梯最直接的动规方法(斐波那契)。 + +**这次终于讲到了背包问题,我选择带录友们再爬一次楼梯!** + 这道题目 我们在[动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html) 中已经讲过一次了,原题其实是一道简单动规的题目。 既然这么简单为什么还要讲呢,其实本题稍加改动就是一道面试好题。 diff --git a/problems/0101.对称二叉树.md b/problems/0101.对称二叉树.md index a0870fdd..30ca3d77 100644 --- a/problems/0101.对称二叉树.md +++ b/problems/0101.对称二叉树.md @@ -426,8 +426,8 @@ class Solution: st.append(root.left) st.append(root.right) while st: - leftNode = st.pop() rightNode = st.pop() + leftNode = st.pop() if not leftNode and not rightNode: continue if not leftNode or not rightNode or leftNode.val != rightNode.val: diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md index aebb6f08..3a4e0a31 100644 --- a/problems/0102.二叉树的层序遍历.md +++ b/problems/0102.二叉树的层序遍历.md @@ -2073,26 +2073,6 @@ class Solution: if curnode.right: queue.append(curnode.right) return root -# 链表解法 -class Solution: - def connect(self, root: 'Node') -> 'Node': - if not root: - return None - first = root - while first: # 遍历每一层 - dummyHead = Node(None) # 为下一行创建一个虚拟头节点,相当于下一行所有节点链表的头结点(每一层都会创建); - tail = dummyHead # 为下一行维护一个尾节点指针(初始化是虚拟节点) - cur = first - while cur: # 遍历当前层的节点 - if cur.left: # 链接下一行的节点 - tail.next = cur.left - tail = tail.next - if cur.right: - tail.next = cur.right - tail = tail.next - cur = cur.next # cur同层移动到下一节点 - first = dummyHead.next # 此处为换行操作,更新到下一行 - return root ``` JavaScript: ```javascript diff --git a/problems/0150.逆波兰表达式求值.md b/problems/0150.逆波兰表达式求值.md index 108c654b..bf90b89b 100644 --- a/problems/0150.逆波兰表达式求值.md +++ b/problems/0150.逆波兰表达式求值.md @@ -89,12 +89,8 @@ C++代码如下: class Solution { public: int evalRPN(vector& tokens) { -<<<<<<< HEAD - stack st; -======= // 力扣修改了后台测试数据,需要用longlong stack st; ->>>>>>> 28f3b52a82e3cc650290fb02030a53900e122f43 for (int i = 0; i < tokens.size(); i++) { if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") { long long num1 = st.top(); diff --git a/problems/0242.有效的字母异位词.md b/problems/0242.有效的字母异位词.md index 556bf39a..f8ebc545 100644 --- a/problems/0242.有效的字母异位词.md +++ b/problems/0242.有效的字母异位词.md @@ -361,7 +361,7 @@ C#: ## 相关题目 -* 383.赎金信 +* [383.赎金信](https://programmercarl.com/0383.%E8%B5%8E%E9%87%91%E4%BF%A1.html) * 49.字母异位词分组 * 438.找到字符串中所有字母异位词 diff --git a/problems/0279.完全平方数.md b/problems/0279.完全平方数.md index 7596da12..6acc4891 100644 --- a/problems/0279.完全平方数.md +++ b/problems/0279.完全平方数.md @@ -3,9 +3,9 @@

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

-# 动态规划:一样的套路,再求一次完全平方数 -## 279.完全平方数 + +# 279.完全平方数 [力扣题目链接](https://leetcode.cn/problems/perfect-squares/) diff --git a/problems/0322.零钱兑换.md b/problems/0322.零钱兑换.md index 7f2e6988..e337717f 100644 --- a/problems/0322.零钱兑换.md +++ b/problems/0322.零钱兑换.md @@ -3,9 +3,8 @@

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

-# 动态规划: 给我个机会,我再兑换一次零钱 -## 322. 零钱兑换 +# 322. 零钱兑换 [力扣题目链接](https://leetcode.cn/problems/coin-change/) diff --git a/problems/0509.斐波那契数.md b/problems/0509.斐波那契数.md index d3c58f44..1fa5ca18 100644 --- a/problems/0509.斐波那契数.md +++ b/problems/0509.斐波那契数.md @@ -32,6 +32,9 @@ F(n) = F(n - 1) + F(n - 2),其中 n > 1 * 0 <= n <= 30 +# 视频讲解 + +**《代码随想录》算法视频公开课:[手把手带你入门动态规划 | leetcode:509.斐波那契数](https://www.bilibili.com/video/BV1f5411K7mo),相信结合视频在看本篇题解,更有助于大家对本题的理解**。 ## 思路 diff --git a/problems/0746.使用最小花费爬楼梯.md b/problems/0746.使用最小花费爬楼梯.md index cec6f704..b6f5a734 100644 --- a/problems/0746.使用最小花费爬楼梯.md +++ b/problems/0746.使用最小花费爬楼梯.md @@ -4,10 +4,15 @@

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

+ + + # 746. 使用最小花费爬楼梯 [力扣题目链接](https://leetcode.cn/problems/min-cost-climbing-stairs/) +**旧题目描述**: + 数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。 每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。 @@ -30,21 +35,32 @@ * cost 的长度范围是 [2, 1000]。 * cost[i] 将会是一个整型数据,范围为 [0, 999] 。 +----------------- + +本题之前的题目描述是很模糊的,看不出来,第一步需要花费体力值,最后一步不用花费,还是说 第一步不花费体力值,最后一步花费。 + +后来力扣改了题目描述,**新题目描述**: + +给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。 + +你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 + +请你计算并返回达到楼梯顶部的最低花费。 + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20221031170131.png) + + ## 思路 -这道题目可以说是昨天[动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html)的花费版本。 +(**在力扣修改了题目描述下,我又重新修改了题解**) -**注意题目描述:每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯** - -所以示例1中只花费一个15 就可以到阶梯顶,最后一步可以理解为 不用花费。 - -读完题大家应该知道指定需要动态规划的,贪心是不可能了。 +修改之后的题意就比较明确了,题目中说 “你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯” 也就是相当于 跳到 下标 0 或者 下标 1 是不花费体力的, 从 下标 0 下标1 开始跳就要花费体力了。 1. 确定dp数组以及下标的含义 使用动态规划,就要有一个数组来记录状态,本题只需要一个一维数组dp[i]就可以了。 -**dp[i]的定义:到达第i个台阶所花费的最少体力为dp[i]**。(注意这里认为是第一步一定是要花费) +**dp[i]的定义:到达第i台阶所花费的最少体力为dp[i]**。 **对于dp数组的定义,大家一定要清晰!** @@ -52,25 +68,27 @@ **可以有两个途径得到dp[i],一个是dp[i-1] 一个是dp[i-2]**。 -那么究竟是选dp[i-1]还是dp[i-2]呢? +dp[i - 1] 跳到 dp[i] 需要花费 dp[i - 1] + cost[i - 1]。 -一定是选最小的,所以dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; +dp[i - 2] 跳到 dp[i] 需要花费 dp[i - 2] + cost[i - 2]。 + +那么究竟是选从dp[i - 1]跳还是从dp[i - 2]跳呢? + +一定是选最小的,所以dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]); -**注意这里为什么是加cost[i],而不是cost[i-1],cost[i-2]之类的**,因为题目中说了:每当你爬上一个阶梯你都要花费对应的体力值 3. dp数组如何初始化 -根据dp数组的定义,dp数组初始化其实是比较难的,因为不可能初始化为第i台阶所花费的最少体力。 +看一下递归公式,dp[i]由dp[i - 1],dp[i - 2]推出,既然初始化所有的dp[i]是不可能的,那么只初始化dp[0]和dp[1]就够了,其他的最终都是dp[0]dp[1]推出。 -那么看一下递归公式,dp[i]由dp[i-1],dp[i-2]推出,既然初始化所有的dp[i]是不可能的,那么只初始化dp[0]和dp[1]就够了,其他的最终都是dp[0]dp[1]推出。 +那么 dp[0] 应该是多少呢? 根据dp数组的定义,到达第0台阶所花费的最小体力为dp[0],那么有同学可能想,那dp[0] 应该是 cost[0],例如 cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] 的话,dp[0] 就是 cost[0] 应该是1。 -所以初始化代码为: +这里就要说名了,本题力扣为什么改题意了,而且修改题意之后 就清晰很多的原因了。 + +新题目描述中明确说了 “你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。” 也就是说 从 到达 第 0 个台阶是不花费的,但从 第0 个台阶 往上跳的话,需要花费 cost[0]。 + +所以初始化 dp[0] = 0,dp[1] = 0; -```CPP -vector dp(cost.size()); -dp[0] = cost[0]; -dp[1] = cost[1]; -``` 4. 确定遍历顺序 @@ -78,11 +96,10 @@ dp[1] = cost[1]; 本题的遍历顺序其实比较简单,简单到很多同学都忽略了思考这一步直接就把代码写出来了。 -因为是模拟台阶,而且dp[i]又dp[i-1]dp[i-2]推出,所以是从前到后遍历cost数组就可以了。 +因为是模拟台阶,而且dp[i]由dp[i-1]dp[i-2]推出,所以是从前到后遍历cost数组就可以了。 -**但是稍稍有点难度的动态规划,其遍历顺序并不容易确定下来**。 - -例如:01背包,都知道两个for循环,一个for遍历物品嵌套一个for遍历背包容量,那么为什么不是一个for遍历背包容量嵌套一个for遍历物品呢? 以及在使用一维dp数组的时候遍历背包容量为什么要倒序呢? +> **但是稍稍有点难度的动态规划,其遍历顺序并不容易确定下来**。 +> 例如:01背包,都知道两个for循环,一个for遍历物品嵌套一个for遍历背包容量,那么为什么不是一个for遍历背包容量嵌套一个for遍历物品呢? 以及在使用一维dp数组的时候遍历背包容量为什么要倒序呢? **这些都是遍历顺序息息相关。当然背包问题后续「代码随想录」都会重点讲解的!** @@ -90,79 +107,12 @@ dp[1] = cost[1]; 拿示例2:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] ,来模拟一下dp数组的状态变化,如下: -![746.使用最小花费爬楼梯](https://img-blog.csdnimg.cn/2021010621363669.png) +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20221026175104.png) 如果大家代码写出来有问题,就把dp数组打印出来,看看和如上推导的是不是一样的。 以上分析完毕,整体C++代码如下: -```CPP -// 版本一 -class Solution { -public: - int minCostClimbingStairs(vector& cost) { - vector dp(cost.size()); - dp[0] = cost[0]; - dp[1] = cost[1]; - for (int i = 2; i < cost.size(); i++) { - dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; - } - // 注意最后一步可以理解为不用花费,所以取倒数第一步,第二步的最少值 - return min(dp[cost.size() - 1], dp[cost.size() - 2]); - } -}; -``` - -* 时间复杂度:O(n) -* 空间复杂度:O(n) - -还可以优化空间复杂度,因为dp[i]就是由前两位推出来的,那么也不用dp数组了,C++代码如下: - -```CPP -// 版本二 -class Solution { -public: - int minCostClimbingStairs(vector& cost) { - int dp0 = cost[0]; - int dp1 = cost[1]; - for (int i = 2; i < cost.size(); i++) { - int dpi = min(dp0, dp1) + cost[i]; - dp0 = dp1; // 记录一下前两位 - dp1 = dpi; - } - return min(dp0, dp1); - } -}; - -``` - -* 时间复杂度:O(n) -* 空间复杂度:O(1) - -**当然我不建议这么写,能写出版本一就可以了,直观简洁!** - -在后序的讲解中,可能我会忽略这种版本二的写法,大家只要知道有这么个写法就可以了哈。 - -## 拓展 - -这道题描述也确实有点魔幻。 - -题目描述为:每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。 - -示例1: - -输入:cost = [10, 15, 20] -输出:15 - - -**从题目描述可以看出:要不是第一步不需要花费体力,要不就是第最后一步不需要花费体力,我个人理解:题意说的其实是第一步是要支付费用的!**。因为是当你爬上一个台阶就要花费对应的体力值! - -所以我定义的dp[i]意思是也是第一步是要花费体力的,最后一步不用花费体力了,因为已经支付了。 - -当然也可以样,定义dp[i]为:第一步是不花费体力,最后一步是花费体力的。 - -所以代码这么写: - ```CPP class Solution { public: @@ -177,9 +127,58 @@ public: } }; ``` +* 时间复杂度:O(n) +* 空间复杂度:O(n) -这么写看上去比较顺,但是就是感觉和题目描述的不太符。哈哈,也没有必要这么细扣题意了,大家只要知道,题目的意思反正就是要不是第一步不花费,要不是最后一步不花费,都可以。 +还可以优化空间复杂度,因为dp[i]就是由前两位推出来的,那么也不用dp数组了,C++代码如下: +```CPP +// 版本二 +class Solution { +public: + int minCostClimbingStairs(vector& cost) { + int dp0 = 0; + int dp1 = 0; + for (int i = 2; i <= cost.size(); i++) { + int dpi = min(dp1 + cost[i - 1], dp0 + cost[i - 2]); + dp0 = dp1; // 记录一下前两位 + dp1 = dpi; + } + return dp1; + } +}; + +``` + +* 时间复杂度:O(n) +* 空间复杂度:O(1) + +当然如果在面试中,能写出版本一就行,除非面试官额外要求 空间复杂度,那么再去思考版本二,因为版本二还是有点绕。版本一才是正常思路。 + + +## 拓展 + +旧力扣描述,如果按照 第一步是花费的,最后一步不花费,那么代码是这么写的,提交也可以通过 + + +```CPP +// 版本一 +class Solution { +public: + int minCostClimbingStairs(vector& cost) { + vector dp(cost.size()); + dp[0] = cost[0]; // 第一步有花费 + dp[1] = cost[1]; + for (int i = 2; i < cost.size(); i++) { + dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; + } + // 注意最后一步可以理解为不用花费,所以取倒数第一步,第二步的最少值 + return min(dp[cost.size() - 1], dp[cost.size() - 2]); + } +}; +``` + +当然如果对 动态规划 理解不够深入的话,拓展内容就别看了,容易越看越懵。 ## 总结 @@ -193,17 +192,18 @@ public: 但我也可以随便选来一道难题讲呗,这其实是最省事的,不用管什么题目顺序,看心情找一道就讲。 -难的是把题目按梯度排好,循序渐进,再按照统一方法论把这些都串起来,哈哈,所以大家不要催我哈,按照我的节奏一步一步来就行啦。 +难的是把题目按梯度排好,循序渐进,再按照统一方法论把这些都串起来,所以大家不要催我哈,按照我的节奏一步一步来就行了。 学算法,认准「代码随想录」,没毛病! -## 其他语言版本 +## 其他语言版本 +以下版本其他语言版本,大多是按照旧力扣题解来写的,欢迎大家在[Github](https://github.com/youngyangyang04/leetcode-master)上[提交pr](https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A),修正一波。 ### Java ```Java -// 方式一:第一步支付费用 +// 方式一:第一步不支付费用 class Solution { public int minCostClimbingStairs(int[] cost) { int len = cost.length; @@ -224,7 +224,7 @@ class Solution { ``` ```Java -// 方式二:第一步不支付费用 +// 方式二:第一步支付费用 class Solution { public int minCostClimbingStairs(int[] cost) { int[] dp = new int[cost.length]; diff --git a/problems/前序/vim.md b/problems/前序/vim.md index c49ff1e3..ce4a0139 100644 --- a/problems/前序/vim.md +++ b/problems/前序/vim.md @@ -1,4 +1,6 @@ -# 人生苦短,我用VIM! +# 人生苦短,我用VIM!| 最强vim配置 + +> Github地址:[https://github.com/youngyangyang04/PowerVim](https://github.com/youngyangyang04/PowerVim) 熟悉我的录友,应该都知道我是vim流,无论是写代码还是写文档(Markdown),都是vim,都没用IDE。 @@ -53,7 +55,7 @@ IDE那么很吃内存,打开个IDE卡半天,用VIM就很轻便了,秒开 |_| \___/ \_/\_/ \___|_| \/ |_|_| |_| |_| ``` -这个配置我开源在Github上,地址:https://github.com/youngyangyang04/PowerVim +这个配置我开源在Github上,地址:[https://github.com/youngyangyang04/PowerVim](https://github.com/youngyangyang04/PowerVim) @@ -92,6 +94,7 @@ sh install.sh ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20211013102249.png) +Github地址:[https://github.com/youngyangyang04/PowerVim](https://github.com/youngyangyang04/PowerVim) 最后,因为这个vim配置因为我一直没有宣传,所以star数量很少,哈哈哈,录友们去给个star吧,真正的开发利器,值得顶起来! diff --git a/problems/贪心算法总结篇.md b/problems/贪心算法总结篇.md index 86cd053f..d70eecaa 100644 --- a/problems/贪心算法总结篇.md +++ b/problems/贪心算法总结篇.md @@ -4,6 +4,9 @@

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

+# 贪心算法总结篇 + + 我刚刚开始讲解贪心系列的时候就说了,贪心系列并不打算严格的从简单到困难这么个顺序来讲解。 因为贪心的简单题可能往往过于简单甚至感觉不到贪心,如果我连续几天讲解简单的贪心,估计录友们一定会不耐烦了,会感觉贪心有啥好学的。