From 4b575a8dab798fc213c56d21891e6d6e7446bbc5 Mon Sep 17 00:00:00 2001 From: fw_qaq <82551626+fwqaaq@users.noreply.github.com> Date: Wed, 7 Dec 2022 23:18:23 +0800 Subject: [PATCH 001/122] =?UTF-8?q?Update=200450.=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=90=9C=E7=B4=A2=E6=A0=91=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E8=8A=82=E7=82=B9.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0450.删除二叉搜索树中的节点.md | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/problems/0450.删除二叉搜索树中的节点.md b/problems/0450.删除二叉搜索树中的节点.md index 0b4048d5..3d9a6050 100644 --- a/problems/0450.删除二叉搜索树中的节点.md +++ b/problems/0450.删除二叉搜索树中的节点.md @@ -657,6 +657,39 @@ object Solution { } ``` +## rust + +```rust +impl Solution { + pub fn delete_node( + root: Option>>, + key: i32, + ) -> Option>> { + root.as_ref()?; + + let mut node = root.as_ref().unwrap().borrow_mut(); + match node.val.cmp(&key) { + std::cmp::Ordering::Less => node.right = Self::delete_node(node.right.clone(), key), + std::cmp::Ordering::Equal => match (node.left.clone(), node.right.clone()) { + (None, None) => return None, + (None, Some(r)) => return Some(r), + (Some(l), None) => return Some(l), + (Some(l), Some(r)) => { + let mut cur = Some(r.clone()); + while let Some(n) = cur.clone().unwrap().borrow().left.clone() { + cur = Some(n); + } + cur.unwrap().borrow_mut().left = Some(l); + return Some(r); + } + }, + std::cmp::Ordering::Greater => node.left = Self::delete_node(node.left.clone(), key), + } + root.clone() + } +} +``` +

From d97f27621c96972c11b0e48b5498c411ac1e85f5 Mon Sep 17 00:00:00 2001 From: fw_qaq <82551626+fwqaaq@users.noreply.github.com> Date: Fri, 9 Dec 2022 15:44:18 +0800 Subject: [PATCH 002/122] =?UTF-8?q?Update=20problems/0450.=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E4=BA=8C=E5=8F=89=E6=90=9C=E7=B4=A2=E6=A0=91=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E8=8A=82=E7=82=B9.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0450.删除二叉搜索树中的节点.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/problems/0450.删除二叉搜索树中的节点.md b/problems/0450.删除二叉搜索树中的节点.md index 3d9a6050..424491b7 100644 --- a/problems/0450.删除二叉搜索树中的节点.md +++ b/problems/0450.删除二叉搜索树中的节点.md @@ -685,7 +685,8 @@ impl Solution { }, std::cmp::Ordering::Greater => node.left = Self::delete_node(node.left.clone(), key), } - root.clone() + drop(node); + root } } ``` From 94f79ada2d4c234a7b8a67cddda169ef3c591ccf Mon Sep 17 00:00:00 2001 From: coffeelize <46947638+coffeelize@users.noreply.github.com> Date: Sun, 19 Mar 2023 11:13:54 +0800 Subject: [PATCH 003/122] =?UTF-8?q?Update=200001.=E4=B8=A4=E6=95=B0?= =?UTF-8?q?=E4=B9=8B=E5=92=8C.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 还有 --> 还要 --- problems/0001.两数之和.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0001.两数之和.md b/problems/0001.两数之和.md index a0acdcbe..a8f8bf17 100644 --- a/problems/0001.两数之和.md +++ b/problems/0001.两数之和.md @@ -41,7 +41,7 @@ 那么我们就应该想到使用哈希法了。 -因为本地,我们不仅要知道元素有没有遍历过,还有知道这个元素对应的下标,**需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适**。 +因为本地,我们不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,**需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适**。 再来看一下使用数组和set来做哈希法的局限。 From 633cb549fb21c4f45a366a973dcc30768a97a03f Mon Sep 17 00:00:00 2001 From: Javie Deng Date: Mon, 20 Mar 2023 12:36:06 +0800 Subject: [PATCH 004/122] =?UTF-8?q?=E4=B8=BA0226.=E7=BF=BB=E8=BD=AC?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91=E6=B7=BB=E5=8A=A0=E4=BA=86C#?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E7=89=88=EF=BC=88=E5=8C=85=E5=90=AB=E9=80=92?= =?UTF-8?q?=E5=BD=92=E5=92=8C=E8=BF=AD=E4=BB=A3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0226.翻转二叉树.md | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/problems/0226.翻转二叉树.md b/problems/0226.翻转二叉树.md index 16a5be57..611e971a 100644 --- a/problems/0226.翻转二叉树.md +++ b/problems/0226.翻转二叉树.md @@ -914,6 +914,53 @@ impl Solution { } ``` +### C# + +```csharp +//递归 +public class Solution { + public TreeNode InvertTree(TreeNode root) { + if (root == null) return root; + + swap(root); + InvertTree(root.left); + InvertTree(root.right); + return root; + } + + public void swap(TreeNode node) { + TreeNode temp = node.left; + node.left = node.right; + node.right = temp; + } +} +``` + +```csharp +//迭代 +public class Solution { + public TreeNode InvertTree(TreeNode root) { + if (root == null) return null; + Stack stack=new Stack(); + stack.Push(root); + while(stack.Count>0) + { + TreeNode node = stack.Pop(); + swap(node); + if(node.right!=null) stack.Push(node.right); + if(node.left!=null) stack.Push(node.left); + } + return root; + } + + public void swap(TreeNode node) { + TreeNode temp = node.left; + node.left = node.right; + node.right = temp; + } +} +``` +

From da2acd7dcf6c0a3fde44bb3801395eb7050f19f3 Mon Sep 17 00:00:00 2001 From: Javie Deng Date: Mon, 20 Mar 2023 13:05:00 +0800 Subject: [PATCH 005/122] =?UTF-8?q?"=E4=B8=BA0226.=E7=BF=BB=E8=BD=AC?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91=E6=B7=BB=E5=8A=A0=E4=BA=86C#?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E7=89=88=EF=BC=88=E5=8C=85=E5=90=AB=E9=80=92?= =?UTF-8?q?=E5=BD=92=E5=92=8C=E8=BF=AD=E4=BB=A3=EF=BC=89"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0226.翻转二叉树.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/problems/0226.翻转二叉树.md b/problems/0226.翻转二叉树.md index 611e971a..c1f0f2c1 100644 --- a/problems/0226.翻转二叉树.md +++ b/problems/0226.翻转二叉树.md @@ -928,7 +928,7 @@ public class Solution { return root; } - public void swap(TreeNode node) { + public void swap(TreeNode node) { TreeNode temp = node.left; node.left = node.right; node.right = temp; @@ -953,7 +953,7 @@ public class Solution { return root; } - public void swap(TreeNode node) { + public void swap(TreeNode node) { TreeNode temp = node.left; node.left = node.right; node.right = temp; From f51f31290f69bf467bdb444d8c85a94b0c30616a Mon Sep 17 00:00:00 2001 From: fw_qaq Date: Thu, 23 Mar 2023 11:10:20 +0800 Subject: [PATCH 006/122] =?UTF-8?q?Update=200406.=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E8=BA=AB=E9=AB=98=E9=87=8D=E5=BB=BA=E9=98=9F=E5=88=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0406.根据身高重建队列.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/problems/0406.根据身高重建队列.md b/problems/0406.根据身高重建队列.md index 48c498e1..2e6818db 100644 --- a/problems/0406.根据身高重建队列.md +++ b/problems/0406.根据身高重建队列.md @@ -291,19 +291,19 @@ var reconstructQueue = function(people) { ```Rust impl Solution { - pub fn reconstruct_queue(people: Vec>) -> Vec> { - let mut people = people; + pub fn reconstruct_queue(mut people: Vec>) -> Vec> { + let mut queue = vec![]; people.sort_by(|a, b| { - if a[0] == b[0] { return a[1].cmp(&b[1]); } + if a[0] == b[0] { + return a[1].cmp(&b[1]); + } b[0].cmp(&a[0]) }); - let mut que: Vec> = Vec::new(); - que.push(people[0].clone()); - for i in 1..people.len() { - let position = people[i][1]; - que.insert(position as usize, people[i].clone()); + queue.push(people[0].clone()); + for v in people.iter().skip(1) { + queue.insert(v[1] as usize, v.clone()); } - que + queue } } ``` From b934f47c83340b91b8a6291d96420978da0ec0e6 Mon Sep 17 00:00:00 2001 From: milu-tao <91822069+milu-tao@users.noreply.github.com> Date: Fri, 24 Mar 2023 22:14:39 +0800 Subject: [PATCH 007/122] =?UTF-8?q?Update=200647.=E5=9B=9E=E6=96=87?= =?UTF-8?q?=E5=AD=90=E4=B8=B2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. result 在遍历过程中 ++ 就行,没必要另外计算 2.题目已经表明:s字符串的长度规则是:1 <= s.length <= 1000,所以前面不需要进行判断字符串长度 --- problems/0647.回文子串.md | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/problems/0647.回文子串.md b/problems/0647.回文子串.md index 90e6da9f..339247f5 100644 --- a/problems/0647.回文子串.md +++ b/problems/0647.回文子串.md @@ -238,33 +238,24 @@ Java: ```java class Solution { public int countSubstrings(String s) { - int len, ans = 0; - if (s == null || (len = s.length()) < 1) return 0; - //dp[i][j]:s字符串下标i到下标j的字串是否是一个回文串,即s[i, j] + char[] chars = s.toCharArray(); + int len = chars.length; boolean[][] dp = new boolean[len][len]; - for (int j = 0; j < len; j++) { - for (int i = 0; i <= j; i++) { - //当两端字母一样时,才可以两端收缩进一步判断 - if (s.charAt(i) == s.charAt(j)) { - //i++,j--,即两端收缩之后i,j指针指向同一个字符或者i超过j了,必然是一个回文串 - if (j - i < 3) { + int result = 0; + for (int i = len - 1; i >= 0; i--) { + for (int j = i; j < len; j++) { + if (chars[i] == chars[j]) { + if (j - i <= 1) { // 情况一 和 情况二 + result++; + dp[i][j] = true; + } else if (dp[i + 1][j - 1]) { //情况三 + result++; dp[i][j] = true; - } else { - //否则通过收缩之后的字串判断 - dp[i][j] = dp[i + 1][j - 1]; } - } else {//两端字符不一样,不是回文串 - dp[i][j] = false; } } } - //遍历每一个字串,统计回文串个数 - for (int i = 0; i < len; i++) { - for (int j = 0; j < len; j++) { - if (dp[i][j]) ans++; - } - } - return ans; + return result; } } ``` From 1d74967090ff2adf96d84ba7f4dbd81d0ed53e68 Mon Sep 17 00:00:00 2001 From: Logen <47022821+Logenleedev@users.noreply.github.com> Date: Thu, 6 Apr 2023 13:40:06 -0500 Subject: [PATCH 008/122] fix typos --- problems/0376.摆动序列.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0376.摆动序列.md b/problems/0376.摆动序列.md index 469c19fd..26baf2f9 100644 --- a/problems/0376.摆动序列.md +++ b/problems/0376.摆动序列.md @@ -99,7 +99,7 @@ 这里我们可以写死,就是 如果只有两个元素,且元素不同,那么结果为 2。 -不写死的话,如果和我们的判断规则结合在一起呢? +不写死的话,如何和我们的判断规则结合在一起呢? 可以假设,数组最前面还有一个数字,那这个数字应该是什么呢? From da4116bcb60b3bf6efa7f44e8e20877e987fadfe Mon Sep 17 00:00:00 2001 From: Lozakaka <102352821+Lozakaka@users.noreply.github.com> Date: Sat, 29 Apr 2023 05:45:01 -0400 Subject: [PATCH 009/122] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20Java=20=E7=B5=B1?= =?UTF-8?q?=E4=B8=80=E8=BF=AD=E4=BB=A3=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 Java 統一迭代法 --- problems/0501.二叉搜索树中的众数.md | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/problems/0501.二叉搜索树中的众数.md b/problems/0501.二叉搜索树中的众数.md index b7ef606f..e467bf8a 100644 --- a/problems/0501.二叉搜索树中的众数.md +++ b/problems/0501.二叉搜索树中的众数.md @@ -472,6 +472,59 @@ class Solution { } } ``` +統一迭代法 +```Java +class Solution { + public int[] findMode(TreeNode root) { + int count = 0; + int maxCount = 0; + TreeNode pre = null; + LinkedList res = new LinkedList<>(); + Stack stack = new Stack<>(); + + if(root != null) + stack.add(root); + + while(!stack.isEmpty()){ + TreeNode curr = stack.peek(); + if(curr != null){ + stack.pop(); + if(curr.right != null) + stack.add(curr.right); + stack.add(curr); + stack.add(null); + if(curr.left != null) + stack.add(curr.left); + }else{ + stack.pop(); + TreeNode temp = stack.pop(); + if(pre == null) + count = 1; + else if(pre != null && pre.val == temp.val) + count++; + else + count = 1; + pre = temp; + if(count == maxCount) + res.add(temp.val); + if(count > maxCount){ + maxCount = count; + res.clear(); + res.add(temp.val); + } + } + } + int[] result = new int[res.size()]; + int i = 0; + for (int x : res){ + result[i] = x; + i++; + } + return result; + } +} +``` + ## Python From 113d1192a92b50ad8154b4b7717cdb5d9186d7a0 Mon Sep 17 00:00:00 2001 From: Mrzhugq <84071063+Mrzhugq@users.noreply.github.com> Date: Sun, 30 Apr 2023 16:45:31 +0800 Subject: [PATCH 010/122] =?UTF-8?q?Update=200035.=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=8F=92=E5=85=A5=E4=BD=8D=E7=BD=AE.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 195行把空间复杂度写成时间复杂度了 --- problems/0035.搜索插入位置.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/problems/0035.搜索插入位置.md b/problems/0035.搜索插入位置.md index 58340c21..a37d67f5 100644 --- a/problems/0035.搜索插入位置.md +++ b/problems/0035.搜索插入位置.md @@ -191,8 +191,8 @@ public: }; ``` -* 时间复杂度:$O(\log n)$ -* 时间复杂度:$O(1)$ +* 时间复杂度:O(log n) +* 空间复杂度:O(1) ## 总结 From bd8ecf7ad1c95a2a8fb928c6ff63bf9503d025a5 Mon Sep 17 00:00:00 2001 From: Jia Tan Date: Sun, 30 Apr 2023 13:40:16 -0700 Subject: [PATCH 011/122] =?UTF-8?q?Update=20=E4=BA=8C=E5=8F=89=E6=A0=91?= =?UTF-8?q?=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 | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/problems/二叉树理论基础.md b/problems/二叉树理论基础.md index 3afedc82..dbf42ca4 100644 --- a/problems/二叉树理论基础.md +++ b/problems/二叉树理论基础.md @@ -195,15 +195,16 @@ Java: ```java public class TreeNode { int val; - TreeNode left; - TreeNode right; - TreeNode() {} - TreeNode(int val) { this.val = val; } - TreeNode(int val, TreeNode left, TreeNode right) { - this.val = val; - this.left = left; - this.right = right; - } + TreeNode left; + TreeNode right; + + TreeNode() {} + TreeNode(int val) { this.val = val; } + TreeNode(int val, TreeNode left, TreeNode right) { + this.val = val; + this.left = left; + this.right = right; + } } ``` @@ -212,10 +213,10 @@ Python: ```python class TreeNode: - def __init__(self, value): - self.value = value - self.left = None - self.right = None + def __init__(self, val, left = None, right = None): + self.val = val + self.left = left + self.right = right ``` Go: From d6b1f3e9538c67b5592a0d93aee4df70a652ab96 Mon Sep 17 00:00:00 2001 From: JaneyLin <105125897+janeyziqinglin@users.noreply.github.com> Date: Tue, 2 May 2023 23:28:44 -0400 Subject: [PATCH 012/122] =?UTF-8?q?Update=200674.=E6=9C=80=E9=95=BF?= =?UTF-8?q?=E8=BF=9E=E7=BB=AD=E9=80=92=E5=A2=9E=E5=BA=8F=E5=88=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix bug. --- problems/0674.最长连续递增序列.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0674.最长连续递增序列.md b/problems/0674.最长连续递增序列.md index 79a8311d..1e79b797 100644 --- a/problems/0674.最长连续递增序列.md +++ b/problems/0674.最长连续递增序列.md @@ -212,7 +212,7 @@ class Solution: return 0 result = 1 dp = [1] * len(nums) - for i in range(len(nums)-1): + for i in range(len(nums)): if nums[i+1] > nums[i]: #连续记录 dp[i+1] = dp[i] + 1 result = max(result, dp[i+1]) From f80761a43833de34dc355d48f729c64ecef63afb Mon Sep 17 00:00:00 2001 From: JaneyLin <105125897+janeyziqinglin@users.noreply.github.com> Date: Tue, 2 May 2023 23:34:57 -0400 Subject: [PATCH 013/122] =?UTF-8?q?Update=200300.=E6=9C=80=E9=95=BF?= =?UTF-8?q?=E4=B8=8A=E5=8D=87=E5=AD=90=E5=BA=8F=E5=88=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit result should be initialized to 1. --- problems/0300.最长上升子序列.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md index e8cb0b5f..01d34949 100644 --- a/problems/0300.最长上升子序列.md +++ b/problems/0300.最长上升子序列.md @@ -149,7 +149,7 @@ class Solution: if len(nums) <= 1: return len(nums) dp = [1] * len(nums) - result = 0 + result = 1 for i in range(1, len(nums)): for j in range(0, i): if nums[i] > nums[j]: From c76ed37fc487bc7da34403bbb484e9e42b582efb Mon Sep 17 00:00:00 2001 From: Lozakaka <102352821+Lozakaka@users.noreply.github.com> Date: Tue, 2 May 2023 23:55:58 -0400 Subject: [PATCH 014/122] adding java iteration method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增java 迭代方法 --- problems/0669.修剪二叉搜索树.md | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/problems/0669.修剪二叉搜索树.md b/problems/0669.修剪二叉搜索树.md index 18d8a0cc..5739f762 100644 --- a/problems/0669.修剪二叉搜索树.md +++ b/problems/0669.修剪二叉搜索树.md @@ -248,6 +248,8 @@ public: ## Java +**递归** + ```Java class Solution { public TreeNode trimBST(TreeNode root, int low, int high) { @@ -269,6 +271,46 @@ class Solution { ``` +**迭代** + +```Java +class Solution { + //iteration + public TreeNode trimBST(TreeNode root, int low, int high) { + if(root == null) + return null; + while(root != null && (root.val < low || root.val > high)){ + if(root.val < low) + root = root.right; + else + root = root.left; + } + + TreeNode curr = root; + + //deal with root's left sub-tree, and deal with the value smaller than low. + while(curr != null){ + while(curr.left != null && curr.left.val < low){ + curr.left = curr.left.right; + } + curr = curr.left; + } + //go back to root; + curr = root; + + //deal with root's righg sub-tree, and deal with the value bigger than high. + while(curr != null){ + while(curr.right != null && curr.right.val > high){ + curr.right = curr.right.left; + } + curr = curr.right; + } + return root; + } +} + +```` + ## Python **递归** From 926008cf1390c596c7225f0f988723fe687a9408 Mon Sep 17 00:00:00 2001 From: Lozakaka <102352821+Lozakaka@users.noreply.github.com> Date: Wed, 3 May 2023 18:08:11 -0400 Subject: [PATCH 015/122] adding java iteraion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增java統一迭代法 --- problems/0538.把二叉搜索树转换为累加树.md | 38 +++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/problems/0538.把二叉搜索树转换为累加树.md b/problems/0538.把二叉搜索树转换为累加树.md index ad5310e1..e60d9a84 100644 --- a/problems/0538.把二叉搜索树转换为累加树.md +++ b/problems/0538.把二叉搜索树转换为累加树.md @@ -177,6 +177,8 @@ public: ## Java +**递归** + ```Java class Solution { int sum; @@ -198,6 +200,42 @@ class Solution { } } ``` +**迭代** + +```Java +class Solution { + //DFS iteraion統一迭代法 + public TreeNode convertBST(TreeNode root) { + int pre = 0; + Stack stack = new Stack<>(); + if(root == null) //edge case check + return null; + + stack.add(root); + + while(!stack.isEmpty()){ + TreeNode curr = stack.peek(); + //curr != null的狀況,只負責存node到stack中 + if(curr != null){ + stack.pop(); + if(curr.left != null) //左 + stack.add(curr.left); + stack.add(curr); //中 + stack.add(null); + if(curr.right != null) //右 + stack.add(curr.right); + }else{ + //curr == null的狀況,只負責做單層邏輯 + stack.pop(); + TreeNode temp = stack.pop(); + temp.val += pre; + pre = temp.val; + } + } + return root; + } +} +``` ## Python **递归** From ae3fc9f57688e6369f593e6dab8e8f1d55a8c919 Mon Sep 17 00:00:00 2001 From: JaneyLin <105125897+janeyziqinglin@users.noreply.github.com> Date: Wed, 3 May 2023 21:02:58 -0400 Subject: [PATCH 016/122] =?UTF-8?q?Update=200053.=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E5=AD=90=E5=BA=8F=E5=92=8C=EF=BC=88=E5=8A=A8=E6=80=81=E8=A7=84?= =?UTF-8?q?=E5=88=92=EF=BC=89.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 题目已定义1 <= nums.length. --- problems/0053.最大子序和(动态规划).md | 2 -- 1 file changed, 2 deletions(-) diff --git a/problems/0053.最大子序和(动态规划).md b/problems/0053.最大子序和(动态规划).md index c7d1b2fd..bd490912 100644 --- a/problems/0053.最大子序和(动态规划).md +++ b/problems/0053.最大子序和(动态规划).md @@ -139,8 +139,6 @@ Python: ```python class Solution: def maxSubArray(self, nums: List[int]) -> int: - if len(nums) == 0: - return 0 dp = [0] * len(nums) dp[0] = nums[0] result = dp[0] From b794dccc5b40c1fa25dc2413edd0bbfda9e20aac Mon Sep 17 00:00:00 2001 From: JaneyLin <105125897+janeyziqinglin@users.noreply.github.com> Date: Wed, 3 May 2023 21:05:55 -0400 Subject: [PATCH 017/122] =?UTF-8?q?Update=200674.=E6=9C=80=E9=95=BF?= =?UTF-8?q?=E8=BF=9E=E7=BB=AD=E9=80=92=E5=A2=9E=E5=BA=8F=E5=88=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0674.最长连续递增序列.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0674.最长连续递增序列.md b/problems/0674.最长连续递增序列.md index 1e79b797..79a8311d 100644 --- a/problems/0674.最长连续递增序列.md +++ b/problems/0674.最长连续递增序列.md @@ -212,7 +212,7 @@ class Solution: return 0 result = 1 dp = [1] * len(nums) - for i in range(len(nums)): + for i in range(len(nums)-1): if nums[i+1] > nums[i]: #连续记录 dp[i+1] = dp[i] + 1 result = max(result, dp[i+1]) From efc06ad7fdebe84477904b05c2e97b0adce6bedc Mon Sep 17 00:00:00 2001 From: Logen <47022821+Logenleedev@users.noreply.github.com> Date: Fri, 5 May 2023 13:57:17 -0500 Subject: [PATCH 018/122] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=B0=E7=9A=84?= =?UTF-8?q?=E8=A7=A3=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0503.下一个更大元素II.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/problems/0503.下一个更大元素II.md b/problems/0503.下一个更大元素II.md index bf651209..3fd4b3b6 100644 --- a/problems/0503.下一个更大元素II.md +++ b/problems/0503.下一个更大元素II.md @@ -164,6 +164,7 @@ class Solution { Python: ```python +# 方法 1: class Solution: def nextGreaterElements(self, nums: List[int]) -> List[int]: dp = [-1] * len(nums) @@ -174,6 +175,26 @@ class Solution: stack.pop() stack.append(i%len(nums)) return dp + +# 方法 2: +class Solution: + def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: + stack = [] + # 创建答案数组 + ans = [-1] * len(nums1) + for i in range(len(nums2)): + while len(stack) > 0 and nums2[i] > nums2[stack[-1]]: + # 判断 num1 是否有 nums2[stack[-1]]。如果没有这个判断会出现指针异常 + if nums2[stack[-1]] in nums1: + # 锁定 num1 检索的 index + index = nums1.index(nums2[stack[-1]]) + # 更新答案数组 + ans[index] = nums2[i] + # 弹出小元素 + # 这个代码一定要放在 if 外面。否则单调栈的逻辑就不成立了 + stack.pop() + stack.append(i) + return ans ``` Go: ```go From 61d479ec47df5d3a1743c4de97ba0e4175954959 Mon Sep 17 00:00:00 2001 From: Lozakaka <102352821+Lozakaka@users.noreply.github.com> Date: Mon, 8 May 2023 15:59:31 -0400 Subject: [PATCH 019/122] improving java solution by using stringBuilder 1. improving java solution by using stringBuilder 2. adding explanation in the code --- problems/0093.复原IP地址.md | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/problems/0093.复原IP地址.md b/problems/0093.复原IP地址.md index 9d4d5918..7b15aad9 100644 --- a/problems/0093.复原IP地址.md +++ b/problems/0093.复原IP地址.md @@ -314,6 +314,47 @@ class Solution { return true; } } +//方法一:但使用stringBuilder,故优化时间、空间复杂度,因为向字符串插入字符时无需复制整个字符串,从而减少了操作的时间复杂度,也不用开新空间存subString,从而减少了空间复杂度。 +class Solution { + List result = new ArrayList<>(); + public List restoreIpAddresses(String s) { + StringBuilder sb = new StringBuilder(s); + backTracking(sb, 0, 0); + return result; + } + private void backTracking(StringBuilder s, int startIndex, int dotCount){ + if(dotCount == 3){ + if(isValid(s, startIndex, s.length() - 1)){ + result.add(s.toString()); + } + return; + } + for(int i = startIndex; i < s.length(); i++){ + if(isValid(s, startIndex, i)){ + s.insert(i + 1, '.'); + backTracking(s, i + 2, dotCount + 1); + s.deleteCharAt(i + 1); + }else{ + break; + } + } + } + //[start, end] + private boolean isValid(StringBuilder s, int start, int end){ + if(start > end) + return false; + if(s.charAt(start) == '0' && start != end) + return false; + int num = 0; + for(int i = start; i <= end; i++){ + int digit = s.charAt(i) - '0'; + num = num * 10 + digit; + if(num > 255) + return false; + } + return true; + } +} //方法二:比上面的方法时间复杂度低,更好地剪枝,优化时间复杂度 class Solution { @@ -358,6 +399,7 @@ class Solution { } ``` + ## python python2: From 40f8230fd86b475e87d970f973042a2b4cbee613 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 9 May 2023 00:20:58 -0500 Subject: [PATCH 020/122] =?UTF-8?q?Update=200106.=E4=BB=8E=E4=B8=AD?= =?UTF-8?q?=E5=BA=8F=E4=B8=8E=E5=90=8E=E5=BA=8F=E9=81=8D=E5=8E=86=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E6=9E=84=E9=80=A0=E4=BA=8C=E5=8F=89=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit python 代码重复 --- .../0106.从中序与后序遍历序列构造二叉树.md | 38 +------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/problems/0106.从中序与后序遍历序列构造二叉树.md b/problems/0106.从中序与后序遍历序列构造二叉树.md index adb374f9..89afad08 100644 --- a/problems/0106.从中序与后序遍历序列构造二叉树.md +++ b/problems/0106.从中序与后序遍历序列构造二叉树.md @@ -400,8 +400,6 @@ public: }; ``` -## Python - # 105.从前序与中序遍历序列构造二叉树 @@ -657,38 +655,6 @@ class Solution { ## Python -```python -class Solution: - def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: - # 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件 - if not postorder: - return - - # 第二步: 后序遍历的最后一个就是当前的中间节点 - root_val = postorder[-1] - root = TreeNode(root_val) - - # 第三步: 找切割点. - root_index = inorder.index(root_val) - - # 第四步: 切割inorder数组. 得到inorder数组的左,右半边. - left_inorder = inorder[:root_index] - right_inorder = inorder[root_index + 1:] - - # 第五步: 切割postorder数组. 得到postorder数组的左,右半边. - # ⭐️ 重点1: 中序数组大小一定跟后序数组大小是相同的. - left_postorder = postorder[:len(left_inorder)] - right_postorder = postorder[len(left_inorder): len(postorder) - 1] - - - # 第六步: 递归 - root.left = self.buildTree(left_inorder, left_postorder) - root.right = self.buildTree(right_inorder, right_postorder) - - # 第七步: 返回答案 - return root -``` - 105.从前序与中序遍历序列构造二叉树 ```python @@ -717,7 +683,7 @@ class Solution: # 第六步: 递归 root.left = self.buildTree(preorder_left, inorder_left) root.right = self.buildTree(preorder_right, inorder_right) - + # 第七步: 返回答案 return root ``` @@ -749,7 +715,7 @@ class Solution: # 第六步: 递归 root.left = self.buildTree(inorder_left, postorder_left) root.right = self.buildTree(inorder_right, postorder_right) - + # 第七步: 返回答案 return root ``` From 7eea41ee0e18381088bce52f7ea218e1ffc78c77 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 9 May 2023 14:04:22 -0500 Subject: [PATCH 021/122] =?UTF-8?q?Update=200654.=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0654.最大二叉树.md | 92 ++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/problems/0654.最大二叉树.md b/problems/0654.最大二叉树.md index 64b38b48..224e75e4 100644 --- a/problems/0654.最大二叉树.md +++ b/problems/0654.最大二叉树.md @@ -259,52 +259,70 @@ class Solution { ``` ### 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 constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode: - if not nums: - return None - maxvalue = max(nums) - index = nums.index(maxvalue) - - root = TreeNode(maxvalue) - - left = nums[:index] - right = nums[index + 1:] - - root.left = self.constructMaximumBinaryTree(left) - root.right = self.constructMaximumBinaryTree(right) - return root + if len(nums) == 1: + return TreeNode(nums[0]) + node = TreeNode(0) + # 找到数组中最大的值和对应的下标 + maxValue = 0 + maxValueIndex = 0 + for i in range(len(nums)): + if nums[i] > maxValue: + maxValue = nums[i] + maxValueIndex = i + node.val = maxValue + # 最大值所在的下标左区间 构造左子树 + if maxValueIndex > 0: + new_list = nums[:maxValueIndex] + node.left = self.constructMaximumBinaryTree(new_list) + # 最大值所在的下标右区间 构造右子树 + if maxValueIndex < len(nums) - 1: + new_list = nums[maxValueIndex+1:] + node.right = self.constructMaximumBinaryTree(new_list) + return node +``` +(版本二) 使用下标 class Solution: - """最大二叉树 递归法""" + def traversal(self, nums: List[int], left: int, right: int) -> TreeNode: + if left >= right: + return None + maxValueIndex = left + for i in range(left + 1, right): + if nums[i] > nums[maxValueIndex]: + maxValueIndex = i + root = TreeNode(nums[maxValueIndex]) + root.left = self.traversal(nums, left, maxValueIndex) + root.right = self.traversal(nums, maxValueIndex + 1, right) + return root def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode: return self.traversal(nums, 0, len(nums)) - - def traversal(self, nums: List[int], begin: int, end: int) -> TreeNode: - # 列表长度为0时返回空节点 - if begin == end: - return None - - # 找到最大的值和其对应的下标 - max_index = begin - for i in range(begin, end): - if nums[i] > nums[max_index]: - max_index = i - - # 构建当前节点 - root = TreeNode(nums[max_index]) - - # 递归构建左右子树 - root.left = self.traversal(nums, begin, max_index) - root.right = self.traversal(nums, max_index + 1, end) - - return root -``` + +``` +(版本三) 使用切片 +class Solution: + def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode: + if not nums: + return None + max_val = max(nums) + max_index = nums.index(max_val) + node = TreeNode(max_val) + node.left = self.constructMaximumBinaryTree(nums[:max_index]) + node.right = self.constructMaximumBinaryTree(nums[max_index+1:]) + return node + +``` ### Go From a45046be8a6387a0c4e7fbf6872c554b6a2f3249 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 9 May 2023 15:11:13 -0500 Subject: [PATCH 022/122] =?UTF-8?q?Update=200617.=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加 创建 root节点 和 python queue的代码优化版本 --- problems/0617.合并二叉树.md | 67 +++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/problems/0617.合并二叉树.md b/problems/0617.合并二叉树.md index f98948f0..18a245c4 100644 --- a/problems/0617.合并二叉树.md +++ b/problems/0617.合并二叉树.md @@ -352,8 +352,7 @@ class Solution { ``` ### Python - -**递归法 - 前序遍历** +(版本一) 递归 - 前序 - 修改root1 ```python # Definition for a binary tree node. # class TreeNode: @@ -377,8 +376,33 @@ class Solution: return root1 # ⚠️ 注意: 本题我们重复使用了题目给出的节点而不是创建新节点. 节省时间, 空间. ``` +(版本二) 递归 - 前序 - 新建root +```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 mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode: + # 递归终止条件: + # 但凡有一个节点为空, 就立刻返回另外一个. 如果另外一个也为None就直接返回None. + if not root1: + return root2 + if not root2: + return root1 + # 上面的递归终止条件保证了代码执行到这里root1, root2都非空. + root = TreeNode() # 创建新节点 + root.val += root1.val + root2.val# 中 + root.left = self.mergeTrees(root1.left, root2.left) #左 + root.right = self.mergeTrees(root1.right, root2.right) # 右 + + return root # ⚠️ 注意: 本题我们创建了新节点. -**迭代法** +``` + +(版本三) 迭代 ```python class Solution: def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode: @@ -413,7 +437,44 @@ class Solution: return root1 ``` +(版本四) 迭代 + 代码优化 +```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 +from collections import deque +class Solution: + def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode: + if not root1: + return root2 + if not root2: + return root1 + + queue = deque() + queue.append((root1, root2)) + + while queue: + node1, node2 = queue.popleft() + node1.val += node2.val + + if node1.left and node2.left: + queue.append((node1.left, node2.left)) + elif not node1.left: + node1.left = node2.left + + if node1.right and node2.right: + queue.append((node1.right, node2.right)) + elif not node1.right: + node1.right = node2.right + + return root1 + + +``` ### Go ```go From 3818de4610acd3a1a3e1a9794f3e6caa4320a9e9 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 9 May 2023 18:50:14 -0500 Subject: [PATCH 023/122] =?UTF-8?q?Update=200700.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=A0=91=E4=B8=AD=E7=9A=84=E6=90=9C=E7=B4=A2?= =?UTF-8?q?.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0700.二叉搜索树中的搜索.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/problems/0700.二叉搜索树中的搜索.md b/problems/0700.二叉搜索树中的搜索.md index 40724f48..13064f97 100644 --- a/problems/0700.二叉搜索树中的搜索.md +++ b/problems/0700.二叉搜索树中的搜索.md @@ -230,7 +230,7 @@ class Solution { ### Python -递归法: +(方法一) 递归 ```python class Solution: @@ -250,12 +250,12 @@ class Solution: ``` -迭代法: +(方法二)迭代 ```python class Solution: def searchBST(self, root: TreeNode, val: int) -> TreeNode: - while root is not None: + while root: if val < root.val: root = root.left elif val > root.val: root = root.right else: return root From 38503ddc59e4554ac2316ca9c6e486b48285df8a Mon Sep 17 00:00:00 2001 From: fwqaaq Date: Fri, 12 May 2023 20:41:46 +0800 Subject: [PATCH 024/122] =?UTF-8?q?Update=201049.=E6=9C=80=E5=90=8E?= =?UTF-8?q?=E4=B8=80=E5=9D=97=E7=9F=B3=E5=A4=B4=E7=9A=84=E9=87=8D=E9=87=8F?= =?UTF-8?q?II.md=20about=20rust?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/1049.最后一块石头的重量II.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/problems/1049.最后一块石头的重量II.md b/problems/1049.最后一块石头的重量II.md index 210ce737..a8150b1f 100644 --- a/problems/1049.最后一块石头的重量II.md +++ b/problems/1049.最后一块石头的重量II.md @@ -379,8 +379,23 @@ object Solution { } ``` +### Rust - +```rust +impl Solution { + pub fn last_stone_weight_ii(stones: Vec) -> i32 { + let sum = stones.iter().sum::(); + let target = sum as usize / 2; + let mut dp = vec![0; target + 1]; + for s in stones { + for j in (s as usize..=target).rev() { + dp[j] = dp[j].max(dp[j - s as usize] + s); + } + } + sum - dp[target] * 2 + } +} +```

From db21086b42d3a7ff1d07dbe4bfbf3e94bd2301b8 Mon Sep 17 00:00:00 2001 From: fwqaaq Date: Fri, 12 May 2023 22:09:19 +0800 Subject: [PATCH 025/122] =?UTF-8?q?Update=200494.=E7=9B=AE=E6=A0=87?= =?UTF-8?q?=E5=92=8C.md=20about=20rust?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0494.目标和.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/problems/0494.目标和.md b/problems/0494.目标和.md index cc2bc8df..e9e33370 100644 --- a/problems/0494.目标和.md +++ b/problems/0494.目标和.md @@ -417,6 +417,32 @@ object Solution { } ``` +### Rust + +```rust +impl Solution { + pub fn find_target_sum_ways(nums: Vec, target: i32) -> i32 { + let sum = nums.iter().sum::(); + if target.abs() > sum { + return 0; + } + if (target + sum) % 2 == 1 { + return 0; + } + let size = (sum + target) as usize / 2; + let mut dp = vec![0; size + 1]; + dp[0] = 1; + for n in nums { + for s in (n as usize..=size).rev() { + // + dp[s] += dp[s - n as usize]; + } + } + dp[size] + } +} +``` +

From 03829cde0d626a5d77609b29d6a1581924e40800 Mon Sep 17 00:00:00 2001 From: fwqaaq Date: Fri, 12 May 2023 22:09:48 +0800 Subject: [PATCH 026/122] Apply suggestions from code review --- problems/0494.目标和.md | 1 - 1 file changed, 1 deletion(-) diff --git a/problems/0494.目标和.md b/problems/0494.目标和.md index e9e33370..c9f88892 100644 --- a/problems/0494.目标和.md +++ b/problems/0494.目标和.md @@ -434,7 +434,6 @@ impl Solution { dp[0] = 1; for n in nums { for s in (n as usize..=size).rev() { - // dp[s] += dp[s - n as usize]; } } From 1ed8111a24113240dc7707bc0cc4e56c575482e3 Mon Sep 17 00:00:00 2001 From: Lozakaka <102352821+Lozakaka@users.noreply.github.com> Date: Mon, 15 May 2023 15:21:03 -0400 Subject: [PATCH 027/122] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20```Java=20?= =?UTF-8?q?=E4=BB=A5=E6=AD=A3=E5=B8=B8=E9=A1=AF=E7=A4=BA=E4=BF=9D=E7=95=99?= =?UTF-8?q?=E5=AD=97=E9=A1=8F=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 ```Java 以正常顯示保留字顏色 --- problems/0225.用队列实现栈.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0225.用队列实现栈.md b/problems/0225.用队列实现栈.md index 41a1ede2..94c79404 100644 --- a/problems/0225.用队列实现栈.md +++ b/problems/0225.用队列实现栈.md @@ -367,7 +367,7 @@ class MyStack { ``` 优化,使用一个 Queue 实现,但用卡哥的逻辑实现 -``` +```Java class MyStack { Queue queue; From 4948ef48f46d516a475f8bf57ab559b5a71513af Mon Sep 17 00:00:00 2001 From: Lozakaka <102352821+Lozakaka@users.noreply.github.com> Date: Wed, 17 May 2023 16:50:29 -0400 Subject: [PATCH 028/122] =?UTF-8?q?=E6=96=B0=E5=A2=9Ejava=E8=A7=A3?= =?UTF-8?q?=E6=B3=95=E4=B8=AD=20lambda=E4=BB=A5=E5=8F=8Alinkedlist.add?= =?UTF-8?q?=E7=9A=84=E8=A8=BB=E9=87=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增java解法中 lambda以及linkedlist.add的註釋 --- problems/0406.根据身高重建队列.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/problems/0406.根据身高重建队列.md b/problems/0406.根据身高重建队列.md index 3f82d0bc..67aef3ec 100644 --- a/problems/0406.根据身高重建队列.md +++ b/problems/0406.根据身高重建队列.md @@ -191,14 +191,14 @@ class Solution { public int[][] reconstructQueue(int[][] people) { // 身高从大到小排(身高相同k小的站前面) Arrays.sort(people, (a, b) -> { - if (a[0] == b[0]) return a[1] - b[1]; - return b[0] - a[0]; + if (a[0] == b[0]) return a[1] - b[1]; // a - b 是升序排列,故在a[0] == b[0]的狀況下,會根據k值升序排列 + return b[0] - a[0]; //b - a 是降序排列,在a[0] != b[0],的狀況會根據h值降序排列 }); LinkedList que = new LinkedList<>(); for (int[] p : people) { - que.add(p[1],p); + que.add(p[1],p); //Linkedlist.add(index, value),會將value插入到指定index裡。 } return que.toArray(new int[people.length][]); From b660360285c0fbf520106b7afd03f049bc0b6300 Mon Sep 17 00:00:00 2001 From: yujiahan2018 Date: Sat, 20 May 2023 19:29:45 +0800 Subject: [PATCH 029/122] =?UTF-8?q?Update=200718.=E6=9C=80=E9=95=BF?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E5=AD=90=E6=95=B0=E7=BB=84.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0718.最长重复子数组.md | 52 ++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/problems/0718.最长重复子数组.md b/problems/0718.最长重复子数组.md index 08be6732..31782d5d 100644 --- a/problems/0718.最长重复子数组.md +++ b/problems/0718.最长重复子数组.md @@ -198,7 +198,57 @@ public: 而且为了让 `if (dp[i][j] > result) result = dp[i][j];` 收集到全部结果,两层for训练一定从0开始遍历,这样需要加上 `&& i > 0 && j > 0`的判断。 -相对于版本一来说还是多写了不少代码。而且逻辑上也复杂了一些。 优势就是dp数组的定义,更直观一点。 +对于基础不牢的小白来说,在推导出转移方程后可能疑惑上述代码为什么要从`i=0,j=0`遍历而不是从`i=1,j=1`开始遍历,原因在于这里如果不是从`i=0,j=0`位置开始遍历,会漏掉如下样例结果: +```txt +nums1 = [70,39,25,40,7] +nums2 = [52,20,67,5,31] +``` + +当然,如果你愿意也可以使用如下代码,与上面那个c++是同一思路: +```java +class Solution { + public int findLength(int[] nums1, int[] nums2) { + int len1 = nums1.length; + int len2 = nums2.length; + int[][] result = new int[len1][len2]; + + int maxresult = Integer.MIN_VALUE; + + for(int i=0;i Date: Sat, 20 May 2023 19:31:51 +0800 Subject: [PATCH 030/122] =?UTF-8?q?Update=200198.=E6=89=93=E5=AE=B6?= =?UTF-8?q?=E5=8A=AB=E8=88=8D.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0198.打家劫舍.md | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/problems/0198.打家劫舍.md b/problems/0198.打家劫舍.md index 6e682ec3..7c8ba4db 100644 --- a/problems/0198.打家劫舍.md +++ b/problems/0198.打家劫舍.md @@ -141,7 +141,36 @@ class Solution { } } -// 空间优化 dp数组只存与计算相关的两次数据 +// 使用滚动数组思想,优化空间 +// 分析本题可以发现,所求结果仅依赖于前两种状态,此时可以使用滚动数组思想将空间复杂度降低为3个空间 +class Solution { + public int rob(int[] nums) { + + int len = nums.length; + + if (len == 0) return 0; + else if (len == 1) return nums[0]; + else if (len == 2) return Math.max(nums[0],nums[1]); + + + int[] result = new int[3]; //存放选择的结果 + result[0] = nums[0]; + result[1] = Math.max(nums[0],nums[1]); + + + for(int i=2;i nums[1] ? nums[0] : nums[1]; + dp[1] = Math.max(nums[0],nums[1]); int res = 0; // 遍历 for (int i = 2; i < nums.length; i++) { - res = (dp[0] + nums[i]) > dp[1] ? (dp[0] + nums[i]) : dp[1]; + res = Math.max((dp[0] + nums[i]) , dp[1] ); dp[0] = dp[1]; dp[1] = res; } From f5329cd4b913d73f049e6b30f7d2a3a8a2a90770 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 22 May 2023 15:38:38 -0500 Subject: [PATCH 031/122] =?UTF-8?q?Update=200110.=E5=B9=B3=E8=A1=A1?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0110.平衡二叉树.md | 48 +++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/problems/0110.平衡二叉树.md b/problems/0110.平衡二叉树.md index a3bc77fb..c0561e10 100644 --- a/problems/0110.平衡二叉树.md +++ b/problems/0110.平衡二叉树.md @@ -553,6 +553,52 @@ class Solution: 迭代法: +```python +class Solution: + def getDepth(self, cur): + st = [] + if cur is not None: + st.append(cur) + depth = 0 + result = 0 + while st: + node = st[-1] + if node is not None: + st.pop() + st.append(node) # 中 + st.append(None) + depth += 1 + if node.right: + st.append(node.right) # 右 + if node.left: + st.append(node.left) # 左 + + else: + node = st.pop() + st.pop() + depth -= 1 + result = max(result, depth) + return result + + def isBalanced(self, root): + st = [] + if root is None: + return True + st.append(root) + while st: + node = st.pop() # 中 + if abs(self.getDepth(node.left) - self.getDepth(node.right)) > 1: + return False + if node.right: + st.append(node.right) # 右(空节点不入栈) + if node.left: + st.append(node.left) # 左(空节点不入栈) + return True + +``` + +迭代法精简版: + ```python class Solution: def isBalanced(self, root: Optional[TreeNode]) -> bool: @@ -576,8 +622,6 @@ class Solution: height_map[real_node] = 1 + max(left, right) return True ``` - - ### Go ```Go From 2024fc272af5811c3c594bf08959af2c9f62371d Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 22 May 2023 16:48:27 -0500 Subject: [PATCH 032/122] =?UTF-8?q?Update=200257.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=A0=91=E7=9A=84=E6=89=80=E6=9C=89=E8=B7=AF=E5=BE=84.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0257.二叉树的所有路径.md | 40 ++++++++++++++----------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/problems/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md index c396f4a0..4cafbded 100644 --- a/problems/0257.二叉树的所有路径.md +++ b/problems/0257.二叉树的所有路径.md @@ -473,31 +473,27 @@ class Solution { 递归法+回溯(版本一) ```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 -import copy -from typing import List, Optional - class Solution: - def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]: - if not root: - return [] + def traversal(self, cur, path, result): + path.append(cur.val) # 中 + if not cur.left and not cur.right: # 到达叶子节点 + sPath = '->'.join(map(str, path)) + result.append(sPath) + return + if cur.left: # 左 + self.traversal(cur.left, path, result) + path.pop() # 回溯 + if cur.right: # 右 + self.traversal(cur.right, path, result) + path.pop() # 回溯 + + def binaryTreePaths(self, root): result = [] - self.generate_paths(root, [], result) + path = [] + if not root: + return result + self.traversal(root, path, result) return result - - def generate_paths(self, node: TreeNode, path: List[int], result: List[str]) -> None: - path.append(node.val) - if not node.left and not node.right: - result.append('->'.join(map(str, path))) - if node.left: - self.generate_paths(node.left, copy.copy(path), result) - if node.right: - self.generate_paths(node.right, copy.copy(path), result) - path.pop() ``` From 9b770de8cccfee4588a4966086aa4f285c347d68 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 22 May 2023 17:07:10 -0500 Subject: [PATCH 033/122] =?UTF-8?q?Update=200257.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=A0=91=E7=9A=84=E6=89=80=E6=9C=89=E8=B7=AF=E5=BE=84.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0257.二叉树的所有路径.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/problems/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md index 4cafbded..195b6f94 100644 --- a/problems/0257.二叉树的所有路径.md +++ b/problems/0257.二叉树的所有路径.md @@ -497,7 +497,7 @@ class Solution: ``` -递归法+回溯(版本二) +递归法+隐形回溯(版本一) ```Python # Definition for a binary tree node. # class TreeNode: @@ -505,7 +505,6 @@ class Solution: # self.val = val # self.left = left # self.right = right -import copy from typing import List, Optional class Solution: @@ -523,13 +522,13 @@ class Solution: if not node.left and not node.right: result.append('->'.join(map(str, path))) else: - self.generate_paths(node.left, copy.copy(path), result) - self.generate_paths(node.right, copy.copy(path), result) - path.pop() + # path[:] 是隐藏回溯 + self.generate_paths(node.left, path[:], result) + self.generate_paths(node.right, path[:], result) ``` -递归法+隐形回溯 +递归法+隐形回溯(版本二) ```Python # Definition for a binary tree node. # class TreeNode: From 1a672740e99c000e44e1c5186bc57184b1f35b58 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 22 May 2023 17:14:09 -0500 Subject: [PATCH 034/122] =?UTF-8?q?Update=200257.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=A0=91=E7=9A=84=E6=89=80=E6=9C=89=E8=B7=AF=E5=BE=84.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0257.二叉树的所有路径.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/problems/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md index 195b6f94..31cbdb56 100644 --- a/problems/0257.二叉树的所有路径.md +++ b/problems/0257.二叉树的所有路径.md @@ -512,19 +512,19 @@ class Solution: if not root: return [] result = [] - self.generate_paths(root, [], result) + self.traversal(root, [], result) return result - def generate_paths(self, node: TreeNode, path: List[int], result: List[str]) -> None: - if not node: + def traversal(self, cur: TreeNode, path: List[int], result: List[str]) -> None: + if not cur: return - path.append(node.val) - if not node.left and not node.right: + path.append(cur.val) + if not cur.left and not cur.right: result.append('->'.join(map(str, path))) - else: - # path[:] 是隐藏回溯 - self.generate_paths(node.left, path[:], result) - self.generate_paths(node.right, path[:], result) + if cur.left: + self.traversal(cur.left, path[:], result) + if cur.right: + self.traversal(cur.right, path[:], result) ``` From 2e81b18c5d92d99e7b50c35f0db76f8b824b3915 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 22 May 2023 19:04:11 -0500 Subject: [PATCH 035/122] =?UTF-8?q?Update=200257.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=A0=91=E7=9A=84=E6=89=80=E6=9C=89=E8=B7=AF=E5=BE=84.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0257.二叉树的所有路径.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/problems/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md index 31cbdb56..7bd56fbd 100644 --- a/problems/0257.二叉树的所有路径.md +++ b/problems/0257.二叉树的所有路径.md @@ -470,7 +470,7 @@ class Solution { ## Python: -递归法+回溯(版本一) +递归法+回溯 ```Python # Definition for a binary tree node. class Solution: @@ -561,16 +561,11 @@ class Solution: 迭代法: ```Python -from collections import deque - - class Solution: - """二叉树的所有路径 迭代法""" def binaryTreePaths(self, root: TreeNode) -> List[str]: # 题目中节点数至少为1 - stack, path_st, result = deque([root]), deque(), [] - path_st.append(str(root.val)) + stack, path_st, result = [root], [str(root.val)], [] while stack: cur = stack.pop() From 117ef697fac9428f8982a9076a9f25445f1d72ee Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 22 May 2023 19:24:48 -0500 Subject: [PATCH 036/122] =?UTF-8?q?Update=200404.=E5=B7=A6=E5=8F=B6?= =?UTF-8?q?=E5=AD=90=E4=B9=8B=E5=92=8C.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0404.左叶子之和.md | 88 ++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/problems/0404.左叶子之和.md b/problems/0404.左叶子之和.md index 617978b7..aa2868df 100644 --- a/problems/0404.左叶子之和.md +++ b/problems/0404.左叶子之和.md @@ -247,8 +247,7 @@ class Solution { ### Python - -**递归后序遍历** +递归 ```python # Definition for a binary tree node. # class TreeNode: @@ -257,47 +256,64 @@ class Solution { # self.left = left # self.right = right class Solution: - def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int: - if not root: + def sumOfLeftLeaves(self, root): + if root is None: + return 0 + if root.left is None and root.right is None: return 0 - # 检查根节点的左子节点是否为叶节点 - if root.left and not root.left.left and not root.left.right: - left_val = root.left.val - else: - left_val = self.sumOfLeftLeaves(root.left) + leftValue = self.sumOfLeftLeaves(root.left) # 左 + if root.left and not root.left.left and not root.left.right: # 左子树是左叶子的情况 + leftValue = root.left.val - # 递归地计算右子树左叶节点的和 - right_val = self.sumOfLeftLeaves(root.right) - - return left_val + right_val + rightValue = self.sumOfLeftLeaves(root.right) # 右 + + sum_val = leftValue + rightValue # 中 + return sum_val +``` +递归精简版 + +```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 sumOfLeftLeaves(self, root): + if root is None: + return 0 + leftValue = 0 + if root.left is not None and root.left.left is None and root.left.right is None: + leftValue = root.left.val + return leftValue + self.sumOfLeftLeaves(root.left) + self.sumOfLeftLeaves(root.right) ``` -**迭代** +迭代法 ```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 sumOfLeftLeaves(self, root: TreeNode) -> int: - """ - Idea: Each time check current node's left node. - If current node don't have one, skip it. - """ - stack = [] - if root: - stack.append(root) - res = 0 - - while stack: - # 每次都把当前节点的左节点加进去. - cur_node = stack.pop() - if cur_node.left and not cur_node.left.left and not cur_node.left.right: - res += cur_node.left.val - - if cur_node.left: - stack.append(cur_node.left) - if cur_node.right: - stack.append(cur_node.right) - - return res + def sumOfLeftLeaves(self, root): + if root is None: + return 0 + st = [root] + result = 0 + while st: + node = st.pop() + if node.left and node.left.left is None and node.left.right is None: + result += node.left.val + if node.right: + st.append(node.right) + if node.left: + st.append(node.left) + return result + ``` ### Go From 1ac1a8c332fde5f37e848577fae4970e46c1d7e9 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 22 May 2023 19:42:19 -0500 Subject: [PATCH 037/122] =?UTF-8?q?Update=200513.=E6=89=BE=E6=A0=91?= =?UTF-8?q?=E5=B7=A6=E4=B8=8B=E8=A7=92=E7=9A=84=E5=80=BC.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0513.找树左下角的值.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/problems/0513.找树左下角的值.md b/problems/0513.找树左下角的值.md index 26768c74..743b0df9 100644 --- a/problems/0513.找树左下角的值.md +++ b/problems/0513.找树左下角的值.md @@ -330,19 +330,24 @@ class Solution: # self.right = right from collections import deque class Solution: - def findBottomLeftValue(self, root: Optional[TreeNode]) -> int: - queue = deque([root]) + def findBottomLeftValue(self, root): + if root is None: + return 0 + queue = deque() + queue.append(root) + result = 0 while queue: size = len(queue) - leftmost = queue[0].val for i in range(size): node = queue.popleft() + if i == 0: + result = node.val if node.left: queue.append(node.left) if node.right: queue.append(node.right) - if not queue: - return leftmost + return result + ``` From 12634b2d89a00e25f2c3897a675c58922c93d0b0 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 22 May 2023 21:48:07 -0500 Subject: [PATCH 038/122] =?UTF-8?q?Update=200654.=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0654.最大二叉树.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/problems/0654.最大二叉树.md b/problems/0654.最大二叉树.md index 224e75e4..07fbeb82 100644 --- a/problems/0654.最大二叉树.md +++ b/problems/0654.最大二叉树.md @@ -292,6 +292,8 @@ class Solution: ``` (版本二) 使用下标 +```python + class Solution: def traversal(self, nums: List[int], left: int, right: int) -> TreeNode: if left >= right: @@ -309,7 +311,8 @@ class Solution: return self.traversal(nums, 0, len(nums)) -``` +```python + (版本三) 使用切片 class Solution: def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode: From 41cf3a4cdd276f52e2039a886ed21f0bdfa50444 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 22 May 2023 21:49:22 -0500 Subject: [PATCH 039/122] =?UTF-8?q?Update=200654.=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0654.最大二叉树.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/problems/0654.最大二叉树.md b/problems/0654.最大二叉树.md index 07fbeb82..980523f5 100644 --- a/problems/0654.最大二叉树.md +++ b/problems/0654.最大二叉树.md @@ -292,7 +292,6 @@ class Solution: ``` (版本二) 使用下标 -```python class Solution: def traversal(self, nums: List[int], left: int, right: int) -> TreeNode: @@ -311,9 +310,11 @@ class Solution: return self.traversal(nums, 0, len(nums)) -```python (版本三) 使用切片 + +```python + class Solution: def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode: if not nums: From 7a544d95b8aa54cbb3e7457458fd4a67304048ae Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 22 May 2023 21:50:17 -0500 Subject: [PATCH 040/122] =?UTF-8?q?Update=200654.=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0654.最大二叉树.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/problems/0654.最大二叉树.md b/problems/0654.最大二叉树.md index 980523f5..33a9176e 100644 --- a/problems/0654.最大二叉树.md +++ b/problems/0654.最大二叉树.md @@ -292,6 +292,7 @@ class Solution: ``` (版本二) 使用下标 +```python class Solution: def traversal(self, nums: List[int], left: int, right: int) -> TreeNode: @@ -309,7 +310,7 @@ class Solution: def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode: return self.traversal(nums, 0, len(nums)) - + ``` (版本三) 使用切片 From 6498ee65f7d9d38e59d3e338bdbec9a79cb6a438 Mon Sep 17 00:00:00 2001 From: zdz <2549626703@qq.com> Date: Tue, 23 May 2023 22:36:43 +0800 Subject: [PATCH 041/122] =?UTF-8?q?fix=EF=BC=9A0123.=E4=B9=B0=E5=8D=96?= =?UTF-8?q?=E8=82=A1=E7=A5=A8=E7=9A=84=E6=9C=80=E4=BD=B3=E6=97=B6=E6=9C=BA?= =?UTF-8?q?III=20=3D=E3=80=8B=E9=94=99=E8=AF=AF=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0123.买卖股票的最佳时机III.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/problems/0123.买卖股票的最佳时机III.md b/problems/0123.买卖股票的最佳时机III.md index af6870d4..6ac9a576 100644 --- a/problems/0123.买卖股票的最佳时机III.md +++ b/problems/0123.买卖股票的最佳时机III.md @@ -242,9 +242,9 @@ class Solution { for (int i = 1; i < len; i++) { dp[i][1] = Math.max(dp[i - 1][1], -prices[i]); - dp[i][2] = Math.max(dp[i - 1][2], dp[i][1] + prices[i]); - dp[i][3] = Math.max(dp[i - 1][3], dp[i][2] - prices[i]); - dp[i][4] = Math.max(dp[i - 1][4], dp[i][3] + prices[i]); + dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]); + dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]); + dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]); } return dp[len - 1][4]; From 3058654e93415b39469468dabf4ac02907d04b71 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 23 May 2023 20:47:45 -0500 Subject: [PATCH 042/122] =?UTF-8?q?Update=200098.=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=90=9C=E7=B4=A2=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0098.验证二叉搜索树.md | 182 ++++++++++++++++---------------- 1 file changed, 89 insertions(+), 93 deletions(-) diff --git a/problems/0098.验证二叉搜索树.md b/problems/0098.验证二叉搜索树.md index 95afe680..f2e148c2 100644 --- a/problems/0098.验证二叉搜索树.md +++ b/problems/0098.验证二叉搜索树.md @@ -341,117 +341,113 @@ class Solution { ## Python -**递归** - 利用BST中序遍历特性,把树"压缩"成数组 +递归法(版本一)利用中序递增性质,转换成数组 ```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 isValidBST(self, root: TreeNode) -> bool: - # 思路: 利用BST中序遍历的特性. - # 中序遍历输出的二叉搜索树节点的数值是有序序列 - candidate_list = [] - - def __traverse(root: TreeNode) -> None: - nonlocal candidate_list - if not root: - return - __traverse(root.left) - candidate_list.append(root.val) - __traverse(root.right) - - def __is_sorted(nums: list) -> bool: - for i in range(1, len(nums)): - if nums[i] <= nums[i - 1]: # ⚠️ 注意: Leetcode定义二叉搜索树中不能有重复元素 - return False - return True - - __traverse(root) - res = __is_sorted(candidate_list) - - return res -``` + def __init__(self): + self.vec = [] -**递归** - 标准做法 + def traversal(self, root): + if root is None: + return + self.traversal(root.left) + self.vec.append(root.val) # 将二叉搜索树转换为有序数组 + self.traversal(root.right) -```python -class Solution: - def isValidBST(self, root: TreeNode) -> bool: - # 规律: BST的中序遍历节点数值是从小到大. - cur_max = -float("INF") - def __isValidBST(root: TreeNode) -> bool: - nonlocal cur_max - - if not root: - return True - - is_left_valid = __isValidBST(root.left) - if cur_max < root.val: - cur_max = root.val - else: + def isValidBST(self, root): + self.vec = [] # 清空数组 + self.traversal(root) + for i in range(1, len(self.vec)): + # 注意要小于等于,搜索树里不能有相同元素 + if self.vec[i] <= self.vec[i - 1]: return False - is_right_valid = __isValidBST(root.right) - - return is_left_valid and is_right_valid - return __isValidBST(root) + return True + ``` -**递归** - 避免初始化最小值做法: + +递归法(版本二)设定极小值,进行比较 + ```python class Solution: - def isValidBST(self, root: TreeNode) -> bool: - # 规律: BST的中序遍历节点数值是从小到大. - pre = None - def __isValidBST(root: TreeNode) -> bool: - nonlocal pre - - if not root: - return True - - is_left_valid = __isValidBST(root.left) - if pre and pre.val>=root.val: return False - pre = root - is_right_valid = __isValidBST(root.right) - - return is_left_valid and is_right_valid - return __isValidBST(root) + def __init__(self): + self.maxVal = float('-inf') # 因为后台测试数据中有int最小值 + + def isValidBST(self, root): + if root is None: + return True + + left = self.isValidBST(root.left) + # 中序遍历,验证遍历的元素是不是从小到大 + if self.maxVal < root.val: + self.maxVal = root.val + else: + return False + right = self.isValidBST(root.right) + + return left and right + ``` +递归法(版本三)直接取该树的最小值 ```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 isValidBST(self, root: TreeNode) -> bool: + def __init__(self): + self.pre = None # 用来记录前一个节点 + + def isValidBST(self, root): + if root is None: + return True + + left = self.isValidBST(root.left) + + if self.pre is not None and self.pre.val >= root.val: + return False + self.pre = root # 记录前一个节点 + + right = self.isValidBST(root.right) + return left and right + + + +``` +迭代法 +```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 isValidBST(self, root): stack = [] cur = root - pre = None - while cur or stack: - if cur: # 指针来访问节点,访问到最底层 + pre = None # 记录前一个节点 + while cur is not None or len(stack) > 0: + if cur is not None: stack.append(cur) - cur = cur.left - else: # 逐一处理节点 - cur = stack.pop() - if pre and cur.val <= pre.val: # 比较当前节点和前节点的值的大小 + cur = cur.left # 左 + else: + cur = stack.pop() # 中 + if pre is not None and cur.val <= pre.val: return False - pre = cur - cur = cur.right + pre = cur # 保存前一个访问的结点 + cur = cur.right # 右 return True ``` -```python -# 遵循Carl的写法,只添加了节点判断的部分 -class Solution: - def isValidBST(self, root: TreeNode) -> bool: - # method 2 - que, pre = [], None - while root or que: - while root: - que.append(root) - root = root.left - root = que.pop() - # 对第一个节点只做记录,对后面的节点进行比较 - if pre is None: - pre = root.val - else: - if pre >= root.val: return False - pre = root.val - root = root.right - return True -``` + ## Go From 85e4c414571db69f8dd4c27c67928632845e65e4 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 23 May 2023 20:56:07 -0500 Subject: [PATCH 043/122] =?UTF-8?q?Update=200530.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=A0=91=E7=9A=84=E6=9C=80=E5=B0=8F=E7=BB=9D?= =?UTF-8?q?=E5=AF=B9=E5=B7=AE.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0530.二叉搜索树的最小绝对差.md | 100 ++++++++++++++---------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/problems/0530.二叉搜索树的最小绝对差.md b/problems/0530.二叉搜索树的最小绝对差.md index fa1430de..3fe57702 100644 --- a/problems/0530.二叉搜索树的最小绝对差.md +++ b/problems/0530.二叉搜索树的最小绝对差.md @@ -204,66 +204,82 @@ class Solution { ``` ## Python -递归 +递归法(版本一)利用中序递增,结合数组 ```python class Solution: - def getMinimumDifference(self, root: TreeNode) -> int: - res = [] - r = float("inf") - def buildaList(root): //把二叉搜索树转换成有序数组 - if not root: return None - if root.left: buildaList(root.left) //左 - res.append(root.val) //中 - if root.right: buildaList(root.right) //右 - return res - - buildaList(root) - for i in range(len(res)-1): // 统计有序数组的最小差值 - r = min(abs(res[i]-res[i+1]),r) - return r - - -class Solution: # 双指针法,不用数组 (同Carl写法) - 更快 - def getMinimumDifference(self, root: Optional[TreeNode]) -> int: - global pre,minval - pre = None - minval = 10**5 - self.traversal(root) - return minval + def __init__(self): + self.vec = [] - def traversal(self,root): - global pre,minval - if not root: return None + def traversal(self, root): + if root is None: + return self.traversal(root.left) - if pre and root.val-pre.val int: + def __init__(self): + self.result = float('inf') + self.pre = None + + def traversal(self, cur): + if cur is None: + return + self.traversal(cur.left) # 左 + if self.pre is not None: # 中 + self.result = min(self.result, cur.val - self.pre.val) + self.pre = cur # 记录前一个 + self.traversal(cur.right) # 右 + + def getMinimumDifference(self, root): + self.traversal(root) + return self.result + + +``` + +迭代法 +```python +class Solution: + def getMinimumDifference(self, root): stack = [] cur = root pre = None result = float('inf') - while cur or stack: - if cur: # 指针来访问节点,访问到最底层 - stack.append(cur) - cur = cur.left - else: # 逐一处理节点 + + while cur is not None or len(stack) > 0: + if cur is not None: + stack.append(cur) # 将访问的节点放进栈 + cur = cur.left # 左 + else: cur = stack.pop() - if pre: # 当前节点和前节点的值的差值 - result = min(result, abs(cur.val - pre.val)) + if pre is not None: # 中 + result = min(result, cur.val - pre.val) pre = cur - cur = cur.right + cur = cur.right # 右 + return result + + ``` - ## Go: 中序遍历,然后计算最小差值 From c5d1845f3f48ff7ccd78319fdb4481033b159df1 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 23 May 2023 21:06:32 -0500 Subject: [PATCH 044/122] =?UTF-8?q?Update=200501.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=A0=91=E4=B8=AD=E7=9A=84=E4=BC=97=E6=95=B0?= =?UTF-8?q?.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0501.二叉搜索树中的众数.md | 144 +++++++++++++++++----------- 1 file changed, 87 insertions(+), 57 deletions(-) diff --git a/problems/0501.二叉搜索树中的众数.md b/problems/0501.二叉搜索树中的众数.md index b7ef606f..6dc8ed83 100644 --- a/problems/0501.二叉搜索树中的众数.md +++ b/problems/0501.二叉搜索树中的众数.md @@ -475,8 +475,7 @@ class Solution { ## Python -> 递归法 -> 常量空间,递归产生的栈不算 +递归法(版本一)利用字典 ```python # Definition for a binary tree node. @@ -485,77 +484,108 @@ class Solution { # self.val = val # self.left = left # self.right = right +from collections import defaultdict + class Solution: - def __init__(self): - self.pre = TreeNode() - self.count = 0 - self.max_count = 0 - self.result = [] + def searchBST(self, cur, freq_map): + if cur is None: + return + freq_map[cur.val] += 1 # 统计元素频率 + self.searchBST(cur.left, freq_map) + self.searchBST(cur.right, freq_map) - def findMode(self, root: TreeNode) -> List[int]: - if not root: return None - self.search_BST(root) - return self.result - - def search_BST(self, cur: TreeNode) -> None: - if not cur: return None - self.search_BST(cur.left) - # 第一个节点 - if not self.pre: - self.count = 1 - # 与前一个节点数值相同 - elif self.pre.val == cur.val: - self.count += 1 - # 与前一个节点数值不相同 - else: - self.count = 1 - self.pre = cur + def findMode(self, root): + freq_map = defaultdict(int) # key:元素,value:出现频率 + result = [] + if root is None: + return result + self.searchBST(root, freq_map) + max_freq = max(freq_map.values()) + for key, freq in freq_map.items(): + if freq == max_freq: + result.append(key) + return result - if self.count == self.max_count: - self.result.append(cur.val) - - if self.count > self.max_count: - self.max_count = self.count - self.result = [cur.val] # 清空self.result,确保result之前的的元素都失效 - - self.search_BST(cur.right) ``` +递归法(版本二)利用二叉搜索树性质 -> 迭代法-中序遍历 -> 利用二叉搜索树特性,在历遍过程中更新结果,一次历遍 -> 但需要使用额外空间存储历遍的节点 ```python class Solution: - def findMode(self, root: TreeNode) -> List[int]: - stack = [] + def __init__(self): + self.maxCount = 0 # 最大频率 + self.count = 0 # 统计频率 + self.pre = None + self.result = [] + + def searchBST(self, cur): + if cur is None: + return + + self.searchBST(cur.left) # 左 + # 中 + if self.pre is None: # 第一个节点 + self.count = 1 + elif self.pre.val == cur.val: # 与前一个节点数值相同 + self.count += 1 + else: # 与前一个节点数值不同 + self.count = 1 + self.pre = cur # 更新上一个节点 + + if self.count == self.maxCount: # 如果与最大值频率相同,放进result中 + self.result.append(cur.val) + + if self.count > self.maxCount: # 如果计数大于最大值频率 + self.maxCount = self.count # 更新最大频率 + self.result = [cur.val] # 很关键的一步,不要忘记清空result,之前result里的元素都失效了 + + self.searchBST(cur.right) # 右 + return + + def findMode(self, root): + self.count = 0 + self.maxCount = 0 + self.pre = None # 记录前一个节点 + self.result = [] + + self.searchBST(root) + return self.result +``` +迭代法 +```python +class Solution: + def findMode(self, root): + st = [] cur = root pre = None - maxCount, count = 0, 0 - res = [] - while cur or stack: - if cur: # 指针来访问节点,访问到最底层 - stack.append(cur) - cur = cur.left - else: # 逐一处理节点 - cur = stack.pop() - if pre == None: # 第一个节点 + maxCount = 0 # 最大频率 + count = 0 # 统计频率 + result = [] + + while cur is not None or st: + if cur is not None: # 指针来访问节点,访问到最底层 + st.append(cur) # 将访问的节点放进栈 + cur = cur.left # 左 + else: + cur = st.pop() + if pre is None: # 第一个节点 count = 1 elif pre.val == cur.val: # 与前一个节点数值相同 count += 1 - else: + else: # 与前一个节点数值不同 count = 1 - if count == maxCount: - res.append(cur.val) - if count > maxCount: - maxCount = count - res.clear() - res.append(cur.val) + + if count == maxCount: # 如果和最大值相同,放进result中 + result.append(cur.val) + + if count > maxCount: # 如果计数大于最大值频率 + maxCount = count # 更新最大频率 + result = [cur.val] # 很关键的一步,不要忘记清空result,之前result里的元素都失效了 pre = cur - cur = cur.right - return res - + cur = cur.right # 右 + + return result ``` ## Go From daa5417db9ef711d219ca8e201c6dd0cd57c9a20 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 23 May 2023 21:11:37 -0500 Subject: [PATCH 045/122] =?UTF-8?q?Update=200236.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=A0=91=E7=9A=84=E6=9C=80=E8=BF=91=E5=85=AC=E5=85=B1=E7=A5=96?= =?UTF-8?q?=E5=85=88.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0236.二叉树的最近公共祖先.md | 45 +++++++++++++++++++-------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/problems/0236.二叉树的最近公共祖先.md b/problems/0236.二叉树的最近公共祖先.md index 33201def..0ebd5566 100644 --- a/problems/0236.二叉树的最近公共祖先.md +++ b/problems/0236.二叉树的最近公共祖先.md @@ -274,25 +274,44 @@ class Solution { ``` ## Python - +递归法(版本一) ```python class Solution: - """二叉树的最近公共祖先 递归法""" - - def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': - if not root or root == p or root == q: + def lowestCommonAncestor(self, root, p, q): + if root == q or root == p or root is None: return root - + left = self.lowestCommonAncestor(root.left, p, q) right = self.lowestCommonAncestor(root.right, p, q) - - if left and right: - return root - if left: - return left - return right -``` + if left is not None and right is not None: + return root + + if left is None and right is not None: + return right + elif left is not None and right is None: + return left + else: + return None +``` +递归法(版本二)精简 +```python +class Solution: + def lowestCommonAncestor(self, root, p, q): + if root == q or root == p or root is None: + return root + + left = self.lowestCommonAncestor(root.left, p, q) + right = self.lowestCommonAncestor(root.right, p, q) + + if left is not None and right is not None: + return root + + if left is None: + return right + return left + +``` ## Go ```Go From e7b7aa8e4a85d49933ddfa14e4911cc8d0f72bfe Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 23 May 2023 21:16:37 -0500 Subject: [PATCH 046/122] =?UTF-8?q?Update=200235.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=A0=91=E7=9A=84=E6=9C=80=E8=BF=91=E5=85=AC?= =?UTF-8?q?=E5=85=B1=E7=A5=96=E5=85=88.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0235.二叉搜索树的最近公共祖先.md | 49 +++++++++++++++++------ 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/problems/0235.二叉搜索树的最近公共祖先.md b/problems/0235.二叉搜索树的最近公共祖先.md index 8353303a..9777bb0b 100644 --- a/problems/0235.二叉搜索树的最近公共祖先.md +++ b/problems/0235.二叉搜索树的最近公共祖先.md @@ -275,34 +275,57 @@ class Solution { ## Python -递归法: +递归法(版本一) ```python class Solution: - """二叉搜索树的最近公共祖先 递归法""" + def traversal(self, cur, p, q): + if cur is None: + return cur + # 中 + if cur.val > p.val and cur.val > q.val: # 左 + left = self.traversal(cur.left, p, q) + if left is not None: + return left - def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': - if root.val > p.val and root.val > q.val: - return self.lowestCommonAncestor(root.left, p, q) - if root.val < p.val and root.val < q.val: - return self.lowestCommonAncestor(root.right, p, q) - return root + if cur.val < p.val and cur.val < q.val: # 右 + right = self.traversal(cur.right, p, q) + if right is not None: + return right + + return cur + + def lowestCommonAncestor(self, root, p, q): + return self.traversal(root, p, q) ``` -迭代法: +迭代法(版本二)精简 ```python class Solution: - """二叉搜索树的最近公共祖先 迭代法""" + def lowestCommonAncestor(self, root, p, q): + if root.val > p.val and root.val > q.val: + return self.lowestCommonAncestor(root.left, p, q) + elif root.val < p.val and root.val < q.val: + return self.lowestCommonAncestor(root.right, p, q) + else: + return root - def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': - while True: +``` + +迭代法 +```python +class Solution: + def lowestCommonAncestor(self, root, p, q): + while root: if root.val > p.val and root.val > q.val: root = root.left elif root.val < p.val and root.val < q.val: root = root.right else: return root -``` + return None + +``` ## Go 递归法: From 511bf447936dbb628609c5b1cad23462f20ac817 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 23 May 2023 21:29:16 -0500 Subject: [PATCH 047/122] =?UTF-8?q?Update=200701.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=A0=91=E4=B8=AD=E7=9A=84=E6=8F=92=E5=85=A5?= =?UTF-8?q?=E6=93=8D=E4=BD=9C.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0701.二叉搜索树中的插入操作.md | 189 ++++++++++-------------- 1 file changed, 80 insertions(+), 109 deletions(-) diff --git a/problems/0701.二叉搜索树中的插入操作.md b/problems/0701.二叉搜索树中的插入操作.md index 4e834201..1ba7461f 100644 --- a/problems/0701.二叉搜索树中的插入操作.md +++ b/problems/0701.二叉搜索树中的插入操作.md @@ -256,132 +256,103 @@ class Solution { ----- ## 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 insertIntoBST(self, root: TreeNode, val: int) -> TreeNode: - # 返回更新后的以当前root为根节点的新树,方便用于更新上一层的父子节点关系链 + def __init__(self): + self.parent = None - # Base Case - if not root: return TreeNode(val) + def traversal(self, cur, val): + if cur is None: + node = TreeNode(val) + if val > self.parent.val: + self.parent.right = node + else: + self.parent.left = node + return - # 单层递归逻辑: - if val < root.val: - # 将val插入至当前root的左子树中合适的位置 - # 并更新当前root的左子树为包含目标val的新左子树 - root.left = self.insertIntoBST(root.left, val) + self.parent = cur + if cur.val > val: + self.traversal(cur.left, val) + if cur.val < val: + self.traversal(cur.right, val) - if root.val < val: - # 将val插入至当前root的右子树中合适的位置 - # 并更新当前root的右子树为包含目标val的新右子树 - root.right = self.insertIntoBST(root.right, val) - - # 返回更新后的以当前root为根节点的新树 + def insertIntoBST(self, root, val): + self.parent = TreeNode(0) + if root is None: + return TreeNode(val) + self.traversal(root, val) return root + ``` -**递归法** - 无返回值 +递归法(版本二) ```python class Solution: - def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode: - if not root: + def insertIntoBST(self, root, val): + if root is None: return TreeNode(val) parent = None - def __traverse(cur: TreeNode, val: int) -> None: - # 在函数运行的同时把新节点插入到该被插入的地方. - nonlocal parent - if not cur: - new_node = TreeNode(val) - if parent.val < val: - parent.right = new_node - else: - parent.left = new_node - return - - parent = cur # 重点: parent的作用只有运行到上面if not cur:才会发挥出来. - if cur.val < val: - __traverse(cur.right, val) - else: - __traverse(cur.left, val) - return - __traverse(root, val) - return root -``` - -**递归法** - 无返回值 - another easier way -```python -class Solution: - def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: - newNode = TreeNode(val) - if not root: return newNode - - if not root.left and val < root.val: - root.left = newNode - if not root.right and val > root.val: - root.right = newNode - - if val < root.val: - self.insertIntoBST(root.left, val) - if val > root.val: - self.insertIntoBST(root.right, val) - - return root -``` - -**递归法** - 无返回值 有注释 不用Helper function -```python -class Solution: - def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: - if not root: # for root==None - return TreeNode(val) - if root.valval: - if root.left==None: # found the parent - root.left = TreeNode(val) - else: # not found, keep searching - self.insertIntoBST(root.left, val) - # return the final tree - return root -``` - -**迭代法** -与无返回值的递归函数的思路大体一致 -```python -class Solution: - def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode: - if not root: - return TreeNode(val) - parent = None # 此步可以省略 cur = root - - # 用while循环不断地找新节点的parent - while cur: - parent = cur # 首先保存当前非空节点作为下一次迭代的父节点 - if cur.val < val: - cur = cur.right - elif cur.val > val: + while cur: + parent = cur + if val < cur.val: cur = cur.left - - # 运行到这意味着已经跳出上面的while循环, - # 同时意味着新节点的parent已经被找到. - # parent已被找到, 新节点已经ready. 把两个节点黏在一起就好了. - if parent.val > val: + else: + cur = cur.right + if val < parent.val: parent.left = TreeNode(val) - else: + else: parent.right = TreeNode(val) - return root +``` + +递归法(版本三) +```python +class Solution: + def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: + if root is None or root.val == val: + return TreeNode(val) + elif root.val > val: + if root.left is None: + root.left = TreeNode(val) + else: + self.insertIntoBST(root.left, val) + elif root.val < val: + if root.right is None: + root.right = TreeNode(val) + else: + self.insertIntoBST(root.right, val) + return root +``` + + + +迭代法 +```python +class Solution: + def insertIntoBST(self, root, val): + if root is None: # 如果根节点为空,创建新节点作为根节点并返回 + node = TreeNode(val) + return node + + cur = root + parent = root # 记录上一个节点,用于连接新节点 + while cur is not None: + parent = cur + if cur.val > val: + cur = cur.left + else: + cur = cur.right + + node = TreeNode(val) + if val < parent.val: + parent.left = node # 将新节点连接到父节点的左子树 + else: + parent.right = node # 将新节点连接到父节点的右子树 + + return root + ``` ----- From 731d1ca8aa61b1b8969ead0c5cf8b7d8b21b55e6 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 23 May 2023 21:38:27 -0500 Subject: [PATCH 048/122] =?UTF-8?q?Update=200450.=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=90=9C=E7=B4=A2=E6=A0=91=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E8=8A=82=E7=82=B9.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0450.删除二叉搜索树中的节点.md | 132 ++++++++++++------------ 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/problems/0450.删除二叉搜索树中的节点.md b/problems/0450.删除二叉搜索树中的节点.md index 2602b528..d4dca1d4 100644 --- a/problems/0450.删除二叉搜索树中的节点.md +++ b/problems/0450.删除二叉搜索树中的节点.md @@ -324,88 +324,90 @@ class Solution { ``` ## Python - +递归法(版本一) ```python class Solution: - def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: - if not root : return None # 节点为空,返回 - if root.val < key : - root.right = self.deleteNode(root.right, key) - elif root.val > key : + def deleteNode(self, root, key): + if root is None: + return root + if root.val == key: + if root.left is None and root.right is None: + return None + elif root.left is None: + return root.right + elif root.right is None: + return root.left + else: + cur = root.right + while cur.left is not None: + cur = cur.left + cur.left = root.left + return root.right + if root.val > key: root.left = self.deleteNode(root.left, key) - else: - # 当前节点的左子树为空,返回当前的右子树 - if not root.left : return root.right - # 当前节点的右子树为空,返回当前的左子树 - if not root.right: return root.left - # 左右子树都不为空,找到右孩子的最左节点 记为p - node = root.right - while node.left : - node = node.left - # 将当前节点的左子树挂在p的左孩子上 - node.left = root.left - # 当前节点的右子树替换掉当前节点,完成当前节点的删除 - root = root.right + if root.val < key: + root.right = self.deleteNode(root.right, key) return root ``` -**普通二叉树的删除方式** +递归法(版本二) ```python class Solution: - def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: - if not root: return root - if root.val == key: - if not root.right: # 这里第二次操作目标值:最终删除的作用 + def deleteNode(self, root, key): + if root is None: # 如果根节点为空,直接返回 + return root + if root.val == key: # 找到要删除的节点 + if root.right is None: # 如果右子树为空,直接返回左子树作为新的根节点 return root.left - tmp = root.right - while tmp.left: - tmp = tmp.left - root.val, tmp.val = tmp.val, root.val # 这里第一次操作目标值:交换目标值其右子树最左面节点。 - - root.left = self.deleteNode(root.left, key) - root.right = self.deleteNode(root.right, key) + cur = root.right + while cur.left: # 找到右子树中的最左节点 + cur = cur.left + root.val, cur.val = cur.val, root.val # 将要删除的节点值与最左节点值交换 + root.left = self.deleteNode(root.left, key) # 在左子树中递归删除目标节点 + root.right = self.deleteNode(root.right, key) # 在右子树中递归删除目标节点 return root + ``` **迭代法** ```python class Solution: - def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: - # 找到节点后分两步,1. 把节点的左子树和右子树连起来,2. 把右子树跟父节点连起来 - # root is None - if not root: return root - p = root - last = None - while p: - if p.val==key: - # 1. connect left to right - # right is not None -> left is None | left is not None - if p.right: - if p.left: - node = p.right - while node.left: - node = node.left - node.left = p.left - right = p.right - else: - # right is None -> right=left - right = p.left - # 2. connect right to last - if last==None: - root = right - elif last.val>key: - last.left = right - else: - last.right = right - # 3. return + def deleteOneNode(self, target: TreeNode) -> TreeNode: + """ + 将目标节点(删除节点)的左子树放到目标节点的右子树的最左面节点的左孩子位置上 + 并返回目标节点右孩子为新的根节点 + 是动画里模拟的过程 + """ + if target is None: + return target + if target.right is None: + return target.left + cur = target.right + while cur.left: + cur = cur.left + cur.left = target.left + return target.right + + def deleteNode(self, root: TreeNode, key: int) -> TreeNode: + if root is None: + return root + cur = root + pre = None # 记录cur的父节点,用来删除cur + while cur: + if cur.val == key: break + pre = cur + if cur.val > key: + cur = cur.left else: - # Update last and continue - last = p - if p.val>key: - p = p.left - else: - p = p.right + cur = cur.right + if pre is None: # 如果搜索树只有头结点 + return self.deleteOneNode(cur) + # pre 要知道是删左孩子还是右孩子 + if pre.left and pre.left.val == key: + pre.left = self.deleteOneNode(cur) + if pre.right and pre.right.val == key: + pre.right = self.deleteOneNode(cur) return root ``` From 53652744d2aa05765a10ce824424ee1c06afc57c Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 23 May 2023 21:43:40 -0500 Subject: [PATCH 049/122] =?UTF-8?q?Update=200669.=E4=BF=AE=E5=89=AA?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=90=9C=E7=B4=A2=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0669.修剪二叉搜索树.md | 86 ++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/problems/0669.修剪二叉搜索树.md b/problems/0669.修剪二叉搜索树.md index 18d8a0cc..95372d61 100644 --- a/problems/0669.修剪二叉搜索树.md +++ b/problems/0669.修剪二叉搜索树.md @@ -271,64 +271,72 @@ class Solution { ## 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 trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode: - ''' - 确认递归函数参数以及返回值:返回更新后剪枝后的当前root节点 - ''' - # Base Case - if not root: return None - - # 单层递归逻辑 + if root is None: + return None if root.val < low: - # 若当前root节点小于左界:只考虑其右子树,用于替代更新后的其本身,抛弃其左子树整体 + # 寻找符合区间 [low, high] 的节点 return self.trimBST(root.right, low, high) - - if high < root.val: - # 若当前root节点大于右界:只考虑其左子树,用于替代更新后的其本身,抛弃其右子树整体 + if root.val > high: + # 寻找符合区间 [low, high] 的节点 return self.trimBST(root.left, low, high) + root.left = self.trimBST(root.left, low, high) # root.left 接入符合条件的左孩子 + root.right = self.trimBST(root.right, low, high) # root.right 接入符合条件的右孩子 + return root - if low <= root.val <= high: - root.left = self.trimBST(root.left, low, high) - root.right = self.trimBST(root.right, low, high) - # 返回更新后的剪枝过的当前节点root - return root ``` - -**迭代** - +递归法(版本二)精简 ```python class Solution: - def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]: - if not root: return root - # 处理头结点,让root移动到[L, R] 范围内,注意是左闭右开 - while root and (root.val < low or root.val > high): - if root.val < low: # 小于L往右走 - root = root.right - else: # 大于R往左走 - root = root.left - # 此时root已经在[L, R] 范围内,处理左孩子元素小于L的情况 + def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode: + if root is None: + return None + if root.val < low: + return self.trimBST(root.right, low, high) + if root.val > high: + return self.trimBST(root.left, low, high) + root.left = self.trimBST(root.left, low, high) + root.right = self.trimBST(root.right, low, high) + return root + + +``` + +迭代法 +```python +class Solution: + def trimBST(self, root: TreeNode, L: int, R: int) -> TreeNode: + if not root: + return None + + # 处理头结点,让root移动到[L, R] 范围内,注意是左闭右闭 + while root and (root.val < L or root.val > R): + if root.val < L: + root = root.right # 小于L往右走 + else: + root = root.left # 大于R往左走 + cur = root + + # 此时root已经在[L, R] 范围内,处理左孩子元素小于L的情况 while cur: - while cur.left and cur.left.val < low: + while cur.left and cur.left.val < L: cur.left = cur.left.right cur = cur.left - # 此时root已经在[L, R] 范围内,处理右孩子大于R的情况 + cur = root + + # 此时root已经在[L, R] 范围内,处理右孩子大于R的情况 while cur: - while cur.right and cur.right.val > high: + while cur.right and cur.right.val > R: cur.right = cur.right.left cur = cur.right + return root + ``` ## Go From 76e3811c5e7d2299ff06c2aeb231f8fa78bcb957 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 23 May 2023 21:46:41 -0500 Subject: [PATCH 050/122] =?UTF-8?q?Update=200108.=E5=B0=86=E6=9C=89?= =?UTF-8?q?=E5=BA=8F=E6=95=B0=E7=BB=84=E8=BD=AC=E6=8D=A2=E4=B8=BA=E4=BA=8C?= =?UTF-8?q?=E5=8F=89=E6=90=9C=E7=B4=A2=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0108.将有序数组转换为二叉搜索树.md | 98 ++++++++++----------- 1 file changed, 45 insertions(+), 53 deletions(-) diff --git a/problems/0108.将有序数组转换为二叉搜索树.md b/problems/0108.将有序数组转换为二叉搜索树.md index 89778421..056ef3e2 100644 --- a/problems/0108.将有序数组转换为二叉搜索树.md +++ b/problems/0108.将有序数组转换为二叉搜索树.md @@ -316,73 +316,65 @@ class Solution { ``` ## 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 sortedArrayToBST(self, nums: List[int]) -> TreeNode: - ''' - 构造二叉树:重点是选取数组最中间元素为分割点,左侧是递归左区间;右侧是递归右区间 - 必然是平衡树 - 左闭右闭区间 - ''' - # 返回根节点 - root = self.traversal(nums, 0, len(nums)-1) - return root - def traversal(self, nums: List[int], left: int, right: int) -> TreeNode: - # Base Case if left > right: return None - # 确定左右界的中心,防越界 mid = left + (right - left) // 2 - # 构建根节点 - mid_root = TreeNode(nums[mid]) - # 构建以左右界的中心为分割点的左右子树 - mid_root.left = self.traversal(nums, left, mid-1) - mid_root.right = self.traversal(nums, mid+1, right) + root = TreeNode(nums[mid]) + root.left = self.traversal(nums, left, mid - 1) + root.right = self.traversal(nums, mid + 1, right) + return root + + def sortedArrayToBST(self, nums: List[int]) -> TreeNode: + root = self.traversal(nums, 0, len(nums) - 1) + return root - # 返回由被传入的左右界定义的某子树的根节点 - return mid_root ``` -**迭代**(左闭右开) +迭代法 ```python +from collections import deque + class Solution: - def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]: - if len(nums) == 0: return None - root = TreeNode() # 初始化 - nodeSt = [root] - leftSt = [0] - rightSt = [len(nums)] - - while nodeSt: - node = nodeSt.pop() # 处理根节点 - left = leftSt.pop() - right = rightSt.pop() - mid = left + (right - left) // 2 - node.val = nums[mid] - - if left < mid: # 处理左区间 - node.left = TreeNode() - nodeSt.append(node.left) - leftSt.append(left) - rightSt.append(mid) - - if right > mid + 1: # 处理右区间 - node.right = TreeNode() - nodeSt.append(node.right) - leftSt.append(mid + 1) - rightSt.append(right) + def sortedArrayToBST(self, nums: List[int]) -> TreeNode: + if len(nums) == 0: + return None + root = TreeNode(0) # 初始根节点 + nodeQue = deque() # 放遍历的节点 + leftQue = deque() # 保存左区间下标 + rightQue = deque() # 保存右区间下标 + + nodeQue.append(root) # 根节点入队列 + leftQue.append(0) # 0为左区间下标初始位置 + rightQue.append(len(nums) - 1) # len(nums) - 1为右区间下标初始位置 + + while nodeQue: + curNode = nodeQue.popleft() + left = leftQue.popleft() + right = rightQue.popleft() + mid = left + (right - left) // 2 + + curNode.val = nums[mid] # 将mid对应的元素给中间节点 + + if left <= mid - 1: # 处理左区间 + curNode.left = TreeNode(0) + nodeQue.append(curNode.left) + leftQue.append(left) + rightQue.append(mid - 1) + + if right >= mid + 1: # 处理右区间 + curNode.right = TreeNode(0) + nodeQue.append(curNode.right) + leftQue.append(mid + 1) + rightQue.append(right) + return root + ``` ## Go From 14acf5a55469e766e05b74f2279132ce2a7b66d7 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Tue, 23 May 2023 21:57:22 -0500 Subject: [PATCH 051/122] =?UTF-8?q?Update=200538.=E6=8A=8A=E4=BA=8C?= =?UTF-8?q?=E5=8F=89=E6=90=9C=E7=B4=A2=E6=A0=91=E8=BD=AC=E6=8D=A2=E4=B8=BA?= =?UTF-8?q?=E7=B4=AF=E5=8A=A0=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0538.把二叉搜索树转换为累加树.md | 53 +++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/problems/0538.把二叉搜索树转换为累加树.md b/problems/0538.把二叉搜索树转换为累加树.md index ad5310e1..ad4decc5 100644 --- a/problems/0538.把二叉搜索树转换为累加树.md +++ b/problems/0538.把二叉搜索树转换为累加树.md @@ -200,8 +200,30 @@ class Solution { ``` ## 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 convertBST(self, root: TreeNode) -> TreeNode: + self.pre = 0 # 记录前一个节点的数值 + self.traversal(root) + return root + def traversal(self, cur): + if cur is None: + return + self.traversal(cur.right) + cur.val += self.pre + self.pre = cur.val + self.traversal(cur.left) + + +``` +递归法(版本二) ```python # Definition for a binary tree node. # class TreeNode: @@ -234,7 +256,32 @@ class Solution: return root ``` -**迭代** +迭代法(版本一) +```python +class Solution: + def __init__(self): + self.pre = 0 # 记录前一个节点的数值 + + def traversal(self, root): + stack = [] + cur = root + while cur or stack: + if cur: + stack.append(cur) + cur = cur.right # 右 + else: + cur = stack.pop() # 中 + cur.val += self.pre + self.pre = cur.val + cur = cur.left # 左 + + def convertBST(self, root): + self.pre = 0 + self.traversal(root) + return root + +``` +迭代法(版本二) ```python class Solution: def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]: From 450b3f2f15ab7afa7b18f835879dc1573249edee Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Wed, 24 May 2023 00:51:14 -0500 Subject: [PATCH 052/122] =?UTF-8?q?Update=200111.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=A0=91=E7=9A=84=E6=9C=80=E5=B0=8F=E6=B7=B1=E5=BA=A6.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0111.二叉树的最小深度.md | 108 ++++++++++++++++-------------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/problems/0111.二叉树的最小深度.md b/problems/0111.二叉树的最小深度.md index 0c086f1b..ef59730e 100644 --- a/problems/0111.二叉树的最小深度.md +++ b/problems/0111.二叉树的最小深度.md @@ -297,36 +297,72 @@ class Solution { ## 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 minDepth(self, root: TreeNode) -> int: - if not root: + def getDepth(self, node): + if node is None: return 0 + leftDepth = self.getDepth(node.left) # 左 + rightDepth = self.getDepth(node.right) # 右 - if not root.left and not root.right: - return 1 + # 当一个左子树为空,右不为空,这时并不是最低点 + if node.left is None and node.right is not None: + return 1 + rightDepth - left_depth = float('inf') - right_depth = float('inf') + # 当一个右子树为空,左不为空,这时并不是最低点 + if node.left is not None and node.right is None: + return 1 + leftDepth - if root.left: - left_depth = self.minDepth(root.left) - if root.right: - right_depth = self.minDepth(root.right) - - return 1 + min(left_depth, right_depth) + result = 1 + min(leftDepth, rightDepth) + return result + + def minDepth(self, root): + return self.getDepth(root) ``` +递归法(版本二) -迭代法: +```python +class Solution: + def minDepth(self, root): + if root is None: + return 0 + if root.left is None and root.right is not None: + return 1 + self.minDepth(root.right) + if root.left is not None and root.right is None: + return 1 + self.minDepth(root.left) + return 1 + min(self.minDepth(root.left), self.minDepth(root.right)) + + +``` +递归法(版本三)前序 + +```python +class Solution: + def __init__(self): + self.result = float('inf') + + def getDepth(self, node, depth): + if node is None: + return + if node.left is None and node.right is None: + self.result = min(self.result, depth) + if node.left: + self.getDepth(node.left, depth + 1) + if node.right: + self.getDepth(node.right, depth + 1) + + def minDepth(self, root): + if root is None: + return 0 + self.getDepth(root, 1) + return self.result + + +``` +迭代法 ```python # Definition for a binary tree node. @@ -359,39 +395,7 @@ class Solution: return depth ``` -迭代法: -```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 minDepth(self, root: TreeNode) -> int: - if not root: - return 0 - - queue = collections.deque([(root, 1)]) - - while queue: - node, depth = queue.popleft() - - # Check if the node is a leaf node - if not node.left and not node.right: - return depth - - # Add left and right child to the queue - if node.left: - queue.append((node.left, depth+1)) - if node.right: - queue.append((node.right, depth+1)) - - return 0 - -``` ## Go From 832897e4bbeb21c5906729257af347f6cd90c97c Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Wed, 24 May 2023 01:56:01 -0500 Subject: [PATCH 053/122] =?UTF-8?q?Update=200110.=E5=B9=B3=E8=A1=A1?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0110.平衡二叉树.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/problems/0110.平衡二叉树.md b/problems/0110.平衡二叉树.md index c0561e10..e10a612a 100644 --- a/problems/0110.平衡二叉树.md +++ b/problems/0110.平衡二叉树.md @@ -536,18 +536,16 @@ class Solution: ```python class Solution: - def isBalanced(self, root: TreeNode) -> bool: - return self.height(root) != -1 - def height(self, node: TreeNode) -> int: - if not node: - return 0 - left = self.height(node.left) - if left == -1: - return -1 - right = self.height(node.right) - if right == -1 or abs(left - right) > 1: - return -1 - return max(left, right) + 1 + def isBalanced(self, root: Optional[TreeNode]) -> bool: + return self.get_hight(root) != -1 + def get_hight(self, node): + if not node: + return 0 + left = self.get_hight(node.left) + right = self.get_hight(node.right) + if left == -1 or right == -1 or abs(left - right) > 1: + return -1 + return max(left, right) + 1 ``` From 2f86a5ce27078eff58b5d6e2765091ed01f04d94 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Wed, 24 May 2023 22:39:35 -0500 Subject: [PATCH 054/122] =?UTF-8?q?Update=200701.=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=A0=91=E4=B8=AD=E7=9A=84=E6=8F=92=E5=85=A5?= =?UTF-8?q?=E6=93=8D=E4=BD=9C.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0701.二叉搜索树中的插入操作.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/problems/0701.二叉搜索树中的插入操作.md b/problems/0701.二叉搜索树中的插入操作.md index 1ba7461f..fc4351ba 100644 --- a/problems/0701.二叉搜索树中的插入操作.md +++ b/problems/0701.二叉搜索树中的插入操作.md @@ -326,7 +326,22 @@ class Solution: return root ``` +递归法(版本四) +```python +class Solution: + def insertIntoBST(self, root, val): + if root is None: + node = TreeNode(val) + return node + if root.val > val: + root.left = self.insertIntoBST(root.left, val) + if root.val < val: + root.right = self.insertIntoBST(root.right, val) + + return root + +``` 迭代法 ```python From 3965f2590ce244b3e3eb922b922e74e58cf6941d Mon Sep 17 00:00:00 2001 From: eeee0717 <70054568+eeee0717@users.noreply.github.com> Date: Thu, 25 May 2023 15:51:34 +0800 Subject: [PATCH 055/122] =?UTF-8?q?=E6=B7=BB=E5=8A=A0C#=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0019.删除链表的倒数第N个节点.md | 22 +++++++++++++++ problems/0142.环形链表II.md | 28 +++++++++++++++++++ problems/0203.移除链表元素.md | 35 ++++++++++++++++++++++++ problems/0977.有序数组的平方.md | 8 ++++++ 4 files changed, 93 insertions(+) diff --git a/problems/0019.删除链表的倒数第N个节点.md b/problems/0019.删除链表的倒数第N个节点.md index c6f5bfc7..84eac96b 100644 --- a/problems/0019.删除链表的倒数第N个节点.md +++ b/problems/0019.删除链表的倒数第N个节点.md @@ -412,6 +412,28 @@ struct ListNode* removeNthFromEnd(struct ListNode* head, int n) { ``` +C#: +```csharp +public class Solution { + public ListNode RemoveNthFromEnd(ListNode head, int n) { + ListNode dummpHead = new ListNode(0); + dummpHead.next = head; + var fastNode = dummpHead; + var slowNode = dummpHead; + while(n-- != 0 && fastNode != null) + { + fastNode = fastNode.next; + } + while(fastNode.next != null) + { + fastNode = fastNode.next; + slowNode = slowNode.next; + } + slowNode.next = slowNode.next.next; + return dummpHead.next; + } +} +```

diff --git a/problems/0142.环形链表II.md b/problems/0142.环形链表II.md index e80a715a..f87d2cd9 100644 --- a/problems/0142.环形链表II.md +++ b/problems/0142.环形链表II.md @@ -437,6 +437,34 @@ object Solution { } ``` +C#: +```CSharp +public class Solution +{ + public ListNode DetectCycle(ListNode head) + { + ListNode fast = head; + ListNode slow = head; + while (fast != null && fast.next != null) + { + slow = slow.next; + fast = fast.next.next; + if (fast == slow) + { + fast = head; + while (fast != slow) + { + fast = fast.next; + slow = slow.next; + } + return fast; + } + } + return null; + } +} +``` +

diff --git a/problems/0203.移除链表元素.md b/problems/0203.移除链表元素.md index 6a0de282..f8751658 100644 --- a/problems/0203.移除链表元素.md +++ b/problems/0203.移除链表元素.md @@ -596,6 +596,41 @@ class Solution { } ``` +C# +```CSharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution +{ + public ListNode RemoveElements(ListNode head, int val) + { + ListNode dummyHead = new ListNode(0,head); + ListNode temp = dummyHead; + while(temp.next != null) + { + if(temp.next.val == val) + { + temp.next = temp.next.next; + } + else + { + temp = temp.next; + } + } + return dummyHead.next; + } +} +``` +

diff --git a/problems/0977.有序数组的平方.md b/problems/0977.有序数组的平方.md index 4fbdd1cd..a316096e 100644 --- a/problems/0977.有序数组的平方.md +++ b/problems/0977.有序数组的平方.md @@ -488,6 +488,14 @@ public class Solution { return result; } } + +C# LINQ: +```csharp +public class Solution { + public int[] SortedSquares(int[] nums) { + return nums.Select(x => x * x).OrderBy(x => x).ToArray(); + } +} ```

From 343e0775eb6c10fc2ff7a487cd92381d577f1fb4 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Fri, 26 May 2023 02:17:43 -0500 Subject: [PATCH 056/122] =?UTF-8?q?Update=200216.=E7=BB=84=E5=90=88?= =?UTF-8?q?=E6=80=BB=E5=92=8CIII.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0216.组合总和III.md | 37 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/problems/0216.组合总和III.md b/problems/0216.组合总和III.md index f631c3cd..5a2c1033 100644 --- a/problems/0216.组合总和III.md +++ b/problems/0216.组合总和III.md @@ -360,28 +360,25 @@ class Solution { ```py class Solution: - def __init__(self): - self.res = [] - self.sum_now = 0 - self.path = [] + def combinationSum3(self, k: int, n: int) -> List[List[int]]: + result = [] # 存放结果集 + self.backtracking(n, k, 0, 1, [], result) + return result - def combinationSum3(self, k: int, n: int) -> [[int]]: - self.backtracking(k, n, 1) - return self.res + def backtracking(self, targetSum, k, currentSum, startIndex, path, result): + if currentSum > targetSum: # 剪枝操作 + return # 如果path的长度等于k但currentSum不等于targetSum,则直接返回 + if len(path) == k: + if currentSum == targetSum: + result.append(path[:]) + return + for i in range(startIndex, 9 - (k - len(path)) + 2): # 剪枝 + currentSum += i # 处理 + path.append(i) # 处理 + self.backtracking(targetSum, k, currentSum, i + 1, path, result) # 注意i+1调整startIndex + currentSum -= i # 回溯 + path.pop() # 回溯 - def backtracking(self, k: int, n: int, start_num: int): - if self.sum_now > n: # 剪枝 - return - if len(self.path) == k: # len(path)==k时不管sum是否等于n都会返回 - if self.sum_now == n: - self.res.append(self.path[:]) - return - for i in range(start_num, 10 - (k - len(self.path)) + 1): - self.path.append(i) - self.sum_now += i - self.backtracking(k, n, i + 1) - self.path.pop() - self.sum_now -= i ``` ## Go From 89e9d75cbf5c1b33cc1b86cb6ee38fc3b0dd382d Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Fri, 26 May 2023 02:24:01 -0500 Subject: [PATCH 057/122] =?UTF-8?q?Update=200077.=E7=BB=84=E5=90=88.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0077.组合.md | 82 +++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 54 deletions(-) diff --git a/problems/0077.组合.md b/problems/0077.组合.md index 9c6d481d..c4da4471 100644 --- a/problems/0077.组合.md +++ b/problems/0077.组合.md @@ -377,68 +377,42 @@ class Solution { ``` ### Python - +未剪枝优化 ```python -class Solution(object): - def combine(self, n, k): - """ - :type n: int - :type k: int - :rtype: List[List[int]] - """ - result = [] - path = [] - def backtracking(n, k, startidx): - if len(path) == k: - result.append(path[:]) - return - - # 剪枝, 最后k - len(path)个节点直接构造结果,无需递归 - last_startidx = n - (k - len(path)) + 1 - - for x in range(startidx, last_startidx + 1): - path.append(x) - backtracking(n, k, x + 1) # 递归 - path.pop() # 回溯 - - backtracking(n, k, 1) +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + result = [] # 存放结果集 + self.backtracking(n, k, 1, [], result) return result + def backtracking(self, n, k, startIndex, path, result): + if len(path) == k: + result.append(path[:]) + return + for i in range(startIndex, n + 1): # 需要优化的地方 + path.append(i) # 处理节点 + self.backtracking(n, k, i + 1, path, result) + path.pop() # 回溯,撤销处理的节点 + ``` + +剪枝优化: + ```python class Solution: def combine(self, n: int, k: int) -> List[List[int]]: - res = [] - path = [] - def backtrack(n, k, StartIndex): - if len(path) == k: - res.append(path[:]) - return - for i in range(StartIndex, n + 1): - path.append(i) - backtrack(n, k, i+1) - path.pop() - backtrack(n, k, 1) - return res -``` + result = [] # 存放结果集 + self.backtracking(n, k, 1, [], result) + return result + def backtracking(self, n, k, startIndex, path, result): + if len(path) == k: + result.append(path[:]) + return + for i in range(startIndex, n - (k - len(path)) + 2): # 优化的地方 + path.append(i) # 处理节点 + self.backtracking(n, k, i + 1, path, result) + path.pop() # 回溯,撤销处理的节点 -剪枝: - -```python -class Solution: - def combine(self, n: int, k: int) -> List[List[int]]: - res=[] #存放符合条件结果的集合 - path=[] #用来存放符合条件结果 - def backtrack(n,k,startIndex): - if len(path) == k: - res.append(path[:]) - return - for i in range(startIndex,n-(k-len(path))+2): #优化的地方 - path.append(i) #处理节点 - backtrack(n,k,i+1) #递归 - path.pop() #回溯,撤销处理的节点 - backtrack(n,k,1) - return res ``` ### Go From 4d48c45107d2286ab9d5bbd6ce1adddac816859a Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Fri, 26 May 2023 02:25:21 -0500 Subject: [PATCH 058/122] =?UTF-8?q?Update=200077.=E7=BB=84=E5=90=88?= =?UTF-8?q?=E4=BC=98=E5=8C=96.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0077.组合优化.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/problems/0077.组合优化.md b/problems/0077.组合优化.md index 9736549c..fc543eea 100644 --- a/problems/0077.组合优化.md +++ b/problems/0077.组合优化.md @@ -179,18 +179,21 @@ Python: ```python class Solution: def combine(self, n: int, k: int) -> List[List[int]]: - res=[] #存放符合条件结果的集合 - path=[] #用来存放符合条件结果 - def backtrack(n,k,startIndex): - if len(path) == k: - res.append(path[:]) - return - for i in range(startIndex,n-(k-len(path))+2): #优化的地方 - path.append(i) #处理节点 - backtrack(n,k,i+1) #递归 - path.pop() #回溯,撤销处理的节点 - backtrack(n,k,1) - return res + result = [] # 存放结果集 + self.backtracking(n, k, 1, [], result) + return result + def backtracking(self, n, k, startIndex, path, result): + if len(path) == k: + result.append(path[:]) + return + for i in range(startIndex, n - (k - len(path)) + 2): # 优化的地方 + path.append(i) # 处理节点 + self.backtracking(n, k, i + 1, path, result) + path.pop() # 回溯,撤销处理的节点 + + + + ``` Go: ```Go From 847c60ac3c93af41eaba8bf8e48de71c5eb728ab Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Fri, 26 May 2023 06:41:01 -0500 Subject: [PATCH 059/122] =?UTF-8?q?Update=200017.=E7=94=B5=E8=AF=9D?= =?UTF-8?q?=E5=8F=B7=E7=A0=81=E7=9A=84=E5=AD=97=E6=AF=8D=E7=BB=84=E5=90=88?= =?UTF-8?q?.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0017.电话号码的字母组合.md | 207 ++++++++++++++++++---------- 1 file changed, 131 insertions(+), 76 deletions(-) diff --git a/problems/0017.电话号码的字母组合.md b/problems/0017.电话号码的字母组合.md index d1135497..9f1473db 100644 --- a/problems/0017.电话号码的字母组合.md +++ b/problems/0017.电话号码的字母组合.md @@ -285,98 +285,153 @@ class Solution { ``` ## Python -**回溯** +回溯 ```python class Solution: def __init__(self): - self.answers: List[str] = [] - self.answer: str = '' - self.letter_map = { - '2': 'abc', - '3': 'def', - '4': 'ghi', - '5': 'jkl', - '6': 'mno', - '7': 'pqrs', - '8': 'tuv', - '9': 'wxyz' - } - - def letterCombinations(self, digits: str) -> List[str]: - self.answers.clear() - if not digits: return [] + self.letterMap = [ + "", # 0 + "", # 1 + "abc", # 2 + "def", # 3 + "ghi", # 4 + "jkl", # 5 + "mno", # 6 + "pqrs", # 7 + "tuv", # 8 + "wxyz" # 9 + ] + self.result = [] + self.s = "" + + def backtracking(self, digits, index): + if index == len(digits): + self.result.append(self.s) + return + digit = int(digits[index]) # 将索引处的数字转换为整数 + letters = self.letterMap[digit] # 获取对应的字符集 + for i in range(len(letters)): + self.s += letters[i] # 处理字符 + self.backtracking(digits, index + 1) # 递归调用,注意索引加1,处理下一个数字 + self.s = self.s[:-1] # 回溯,删除最后添加的字符 + + def letterCombinations(self, digits): + if len(digits) == 0: + return self.result self.backtracking(digits, 0) - return self.answers - - def backtracking(self, digits: str, index: int) -> None: - # 回溯函数没有返回值 - # Base Case - if index == len(digits): # 当遍历穷尽后的下一层时 - self.answers.append(self.answer) - return - # 单层递归逻辑 - letters: str = self.letter_map[digits[index]] - for letter in letters: - self.answer += letter # 处理 - self.backtracking(digits, index + 1) # 递归至下一层 - self.answer = self.answer[:-1] # 回溯 + return self.result + ``` -**回溯简化** +回溯精简(版本一) ```python class Solution: def __init__(self): - self.answers: List[str] = [] - self.letter_map = { - '2': 'abc', - '3': 'def', - '4': 'ghi', - '5': 'jkl', - '6': 'mno', - '7': 'pqrs', - '8': 'tuv', - '9': 'wxyz' - } - - def letterCombinations(self, digits: str) -> List[str]: - self.answers.clear() - if not digits: return [] - self.backtracking(digits, 0, '') - return self.answers + self.letterMap = [ + "", # 0 + "", # 1 + "abc", # 2 + "def", # 3 + "ghi", # 4 + "jkl", # 5 + "mno", # 6 + "pqrs", # 7 + "tuv", # 8 + "wxyz" # 9 + ] + self.result = [] - def backtracking(self, digits: str, index: int, answer: str) -> None: - # 回溯函数没有返回值 - # Base Case - if index == len(digits): # 当遍历穷尽后的下一层时 - self.answers.append(answer) - return - # 单层递归逻辑 - letters: str = self.letter_map[digits[index]] + def getCombinations(self, digits, index, s): + if index == len(digits): + self.result.append(s) + return + digit = int(digits[index]) + letters = self.letterMap[digit] for letter in letters: - self.backtracking(digits, index + 1, answer + letter) # 递归至下一层 + 回溯 + self.getCombinations(digits, index + 1, s + letter) + + def letterCombinations(self, digits): + if len(digits) == 0: + return self.result + self.getCombinations(digits, 0, "") + return self.result + ``` -**使用itertools** +回溯精简(版本二) ```python class Solution: - def letterCombinations(self, digits: str) -> List[str]: - import itertools - if not digits: - return list() - - phoneMap = { - "2": "abc", - "3": "def", - "4": "ghi", - "5": "jkl", - "6": "mno", - "7": "pqrs", - "8": "tuv", - "9": "wxyz", - } + def __init__(self): + self.letterMap = [ + "", # 0 + "", # 1 + "abc", # 2 + "def", # 3 + "ghi", # 4 + "jkl", # 5 + "mno", # 6 + "pqrs", # 7 + "tuv", # 8 + "wxyz" # 9 + ] + + def getCombinations(self, digits, index, s, result): + if index == len(digits): + result.append(s) + return + digit = int(digits[index]) + letters = self.letterMap[digit] + for letter in letters: + self.getCombinations(digits, index + 1, s + letter, result) + + def letterCombinations(self, digits): + result = [] + if len(digits) == 0: + return result + self.getCombinations(digits, 0, "", result) + return result + - groups = (phoneMap[digit] for digit in digits) - return ["".join(combination) for combination in itertools.product(*groups)] ``` +回溯优化使用列表 +```python +class Solution: + def __init__(self): + self.letterMap = [ + "", # 0 + "", # 1 + "abc", # 2 + "def", # 3 + "ghi", # 4 + "jkl", # 5 + "mno", # 6 + "pqrs", # 7 + "tuv", # 8 + "wxyz" # 9 + ] + + def getCombinations(self, digits, index, path, result): + if index == len(digits): + result.append(''.join(path)) + return + digit = int(digits[index]) + letters = self.letterMap[digit] + for letter in letters: + path.append(letter) + self.getCombinations(digits, index + 1, path, result) + path.pop() + + def letterCombinations(self, digits): + result = [] + if len(digits) == 0: + return result + self.getCombinations(digits, 0, [], result) + return result + + + +``` + + ## Go From 2c514203087e51aa244ca3df18108e44d64f3f89 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Fri, 26 May 2023 10:28:01 -0500 Subject: [PATCH 060/122] =?UTF-8?q?Update=200039.=E7=BB=84=E5=90=88?= =?UTF-8?q?=E6=80=BB=E5=92=8C.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0039.组合总和.md | 136 +++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 55 deletions(-) diff --git a/problems/0039.组合总和.md b/problems/0039.组合总和.md index c4ee5ca6..b6034755 100644 --- a/problems/0039.组合总和.md +++ b/problems/0039.组合总和.md @@ -271,75 +271,101 @@ class Solution { ## Python -**回溯** +回溯(版本一) ```python class Solution: - def __init__(self): - self.path = [] - self.paths = [] - def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: - ''' - 因为本题没有组合数量限制,所以只要元素总和大于target就算结束 - ''' - self.path.clear() - self.paths.clear() - self.backtracking(candidates, target, 0, 0) - return self.paths - - def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None: - # Base Case - if sum_ == target: - self.paths.append(self.path[:]) # 因为是shallow copy,所以不能直接传入self.path + def backtracking(self, candidates, target, total, startIndex, path, result): + if total > target: return - if sum_ > target: + if total == target: + result.append(path[:]) return - # 单层递归逻辑 - for i in range(start_index, len(candidates)): - sum_ += candidates[i] - self.path.append(candidates[i]) - self.backtracking(candidates, target, sum_, i) # 因为无限制重复选取,所以不是i+1 - sum_ -= candidates[i] # 回溯 - self.path.pop() # 回溯 + for i in range(startIndex, len(candidates)): + total += candidates[i] + path.append(candidates[i]) + self.backtracking(candidates, target, total, i, path, result) # 不用i+1了,表示可以重复读取当前的数 + total -= candidates[i] + path.pop() + + def combinationSum(self, candidates, target): + result = [] + self.backtracking(candidates, target, 0, 0, [], result) + return result + ``` -**剪枝回溯** +回溯剪枝(版本一) ```python class Solution: - def __init__(self): - self.path = [] - self.paths = [] - def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: - ''' - 因为本题没有组合数量限制,所以只要元素总和大于target就算结束 - ''' - self.path.clear() - self.paths.clear() - - # 为了剪枝需要提前进行排序 - candidates.sort() - self.backtracking(candidates, target, 0, 0) - return self.paths - - def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None: - # Base Case - if sum_ == target: - self.paths.append(self.path[:]) # 因为是shallow copy,所以不能直接传入self.path + def backtracking(self, candidates, target, total, startIndex, path, result): + if total == target: + result.append(path[:]) return - # 单层递归逻辑 - # 如果本层 sum + condidates[i] > target,就提前结束遍历,剪枝 - for i in range(start_index, len(candidates)): - if sum_ + candidates[i] > target: - return - sum_ += candidates[i] - self.path.append(candidates[i]) - self.backtracking(candidates, target, sum_, i) # 因为无限制重复选取,所以不是i-1 - sum_ -= candidates[i] # 回溯 - self.path.pop() # 回溯 + + for i in range(startIndex, len(candidates)): + if total + candidates[i] > target: + break + total += candidates[i] + path.append(candidates[i]) + self.backtracking(candidates, target, total, i, path, result) + total -= candidates[i] + path.pop() + + def combinationSum(self, candidates, target): + result = [] + candidates.sort() # 需要排序 + self.backtracking(candidates, target, 0, 0, [], result) + return result + +``` + +回溯(版本二) + +```python +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + result =[] + self.backtracking(candidates, target, 0, [], result) + return result + def backtracking(self, candidates, target, startIndex, path, result): + if target == 0: + result.append(path[:]) + return + if target < 0: + return + for i in range(startIndex, len(candidates)): + path.append(candidates[i]) + self.backtracking(candidates, target - candidates[i], i, path, result) + path.pop() + +``` + +回溯剪枝(版本二) + +```python +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + result =[] + candidates.sort() + self.backtracking(candidates, target, 0, [], result) + return result + def backtracking(self, candidates, target, startIndex, path, result): + if target == 0: + result.append(path[:]) + return + + for i in range(startIndex, len(candidates)): + if target - candidates[i] < 0: + break + path.append(candidates[i]) + self.backtracking(candidates, target - candidates[i], i, path, result) + path.pop() + ``` ## Go From 20db57f364270b9c6b9782b1bdaa675c6e1f7152 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Fri, 26 May 2023 11:10:46 -0500 Subject: [PATCH 061/122] =?UTF-8?q?Update=200040.=E7=BB=84=E5=90=88?= =?UTF-8?q?=E6=80=BB=E5=92=8CII.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0040.组合总和II.md | 143 ++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 72 deletions(-) diff --git a/problems/0040.组合总和II.md b/problems/0040.组合总和II.md index 83708df7..be72d903 100644 --- a/problems/0040.组合总和II.md +++ b/problems/0040.组合总和II.md @@ -354,93 +354,92 @@ class Solution { ``` ## Python -**回溯+巧妙去重(省去使用used** +回溯 ```python class Solution: - def __init__(self): - self.paths = [] - self.path = [] - def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: - ''' - 类似于求三数之和,求四数之和,为了避免重复组合,需要提前进行数组排序 - ''' - self.paths.clear() - self.path.clear() - # 必须提前进行数组排序,避免重复 - candidates.sort() - self.backtracking(candidates, target, 0, 0) - return self.paths - def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None: - # Base Case - if sum_ == target: - self.paths.append(self.path[:]) + def backtracking(self, candidates, target, total, startIndex, path, result): + if total == target: + result.append(path[:]) return - - # 单层递归逻辑 - for i in range(start_index, len(candidates)): - # 剪枝,同39.组合总和 - if sum_ + candidates[i] > target: - return - - # 跳过同一树层使用过的元素 - if i > start_index and candidates[i] == candidates[i-1]: + + for i in range(startIndex, len(candidates)): + if i > startIndex and candidates[i] == candidates[i - 1]: continue - - sum_ += candidates[i] - self.path.append(candidates[i]) - self.backtracking(candidates, target, sum_, i+1) - self.path.pop() # 回溯,为了下一轮for loop - sum_ -= candidates[i] # 回溯,为了下一轮for loop + + if total + candidates[i] > target: + break + + total += candidates[i] + path.append(candidates[i]) + self.backtracking(candidates, target, total, i + 1, path, result) + total -= candidates[i] + path.pop() + + def combinationSum2(self, candidates, target): + result = [] + candidates.sort() + self.backtracking(candidates, target, 0, 0, [], result) + return result + ``` -**回溯+去重(使用used)** +回溯+去重(使用used) ```python class Solution: - def __init__(self): - self.paths = [] - self.path = [] - self.used = [] - def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: - ''' - 类似于求三数之和,求四数之和,为了避免重复组合,需要提前进行数组排序 - 本题需要使用used,用来标记区别同一树层的元素使用重复情况:注意区分递归纵向遍历遇到的重复元素,和for循环遇到的重复元素,这两者的区别 - ''' - self.paths.clear() - self.path.clear() - self.usage_list = [False] * len(candidates) - # 必须提前进行数组排序,避免重复 - candidates.sort() - self.backtracking(candidates, target, 0, 0) - return self.paths - def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None: - # Base Case - if sum_ == target: - self.paths.append(self.path[:]) + def backtracking(self, candidates, target, total, startIndex, used, path, result): + if total == target: + result.append(path[:]) return - - # 单层递归逻辑 - for i in range(start_index, len(candidates)): - # 剪枝,同39.组合总和 - if sum_ + candidates[i] > target: - return - - # 检查同一树层是否出现曾经使用过的相同元素 - # 若数组中前后元素值相同,但前者却未被使用(used == False),说明是for loop中的同一树层的相同元素情况 - if i > 0 and candidates[i] == candidates[i-1] and self.usage_list[i-1] == False: + + for i in range(startIndex, len(candidates)): + # 对于相同的数字,只选择第一个未被使用的数字,跳过其他相同数字 + if i > startIndex and candidates[i] == candidates[i - 1] and not used[i - 1]: continue - sum_ += candidates[i] - self.path.append(candidates[i]) - self.usage_list[i] = True - self.backtracking(candidates, target, sum_, i+1) - self.usage_list[i] = False # 回溯,为了下一轮for loop - self.path.pop() # 回溯,为了下一轮for loop - sum_ -= candidates[i] # 回溯,为了下一轮for loop -``` + if total + candidates[i] > target: + break + total += candidates[i] + path.append(candidates[i]) + used[i] = True + self.backtracking(candidates, target, total, i + 1, used, path, result) + used[i] = False + total -= candidates[i] + path.pop() + + def combinationSum2(self, candidates, target): + used = [False] * len(candidates) + result = [] + candidates.sort() + self.backtracking(candidates, target, 0, 0, used, [], result) + return result + +``` +回溯优化 +```python +class Solution: + def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: + candidates.sort() + results = [] + self.combinationSumHelper(candidates, target, 0, [], results) + return results + + def combinationSumHelper(self, candidates, target, index, path, results): + if target == 0: + results.append(path[:]) + return + for i in range(index, len(candidates)): + if i > index and candidates[i] == candidates[i - 1]: + continue + if candidates[i] > target: + break + path.append(candidates[i]) + self.combinationSumHelper(candidates, target - candidates[i], i + 1, path, results) + path.pop() +``` ## Go 主要在于如何在回溯中去重 From 59742e5544055ea3f4830ff5fea98f7e26671385 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Fri, 26 May 2023 11:11:44 -0500 Subject: [PATCH 062/122] =?UTF-8?q?Update=200040.=E7=BB=84=E5=90=88?= =?UTF-8?q?=E6=80=BB=E5=92=8CII.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0040.组合总和II.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0040.组合总和II.md b/problems/0040.组合总和II.md index be72d903..1dd02b69 100644 --- a/problems/0040.组合总和II.md +++ b/problems/0040.组合总和II.md @@ -384,7 +384,7 @@ class Solution: return result ``` -回溯+去重(使用used) +回溯 使用used ```python class Solution: From a4dcbe67217574e6fd197ad0c9598177faec79e9 Mon Sep 17 00:00:00 2001 From: Lozakaka <102352821+Lozakaka@users.noreply.github.com> Date: Fri, 26 May 2023 18:05:09 -0400 Subject: [PATCH 063/122] =?UTF-8?q?=E6=96=B0=E5=A2=9Ejava=E4=BA=8C?= =?UTF-8?q?=E7=B6=AD"=E6=95=B4=E6=95=B8"=E6=95=B8=E7=B5=84=E7=9A=84?= =?UTF-8?q?=E8=A7=A3=E6=B3=95=E3=80=81=E5=84=AA=E5=8C=96=E4=B8=80=E7=B6=AD?= =?UTF-8?q?=E6=95=B8=E7=B5=84=E7=9A=84=E8=A7=A3=E6=B3=95(=E5=89=AA?= =?UTF-8?q?=E6=9E=9D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增java二維"整數"數組的解法,原本是用boolean數組,有一點繞,故附上二維"整數"數組。 2. 優化一維數組的解法(剪枝),在每一次內層循環完後,立刻檢查dp[target] == target。實測時間複雜度: 26ms -> 20ms --- problems/0416.分割等和子集.md | 61 ++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md index 8115e18e..54d90612 100644 --- a/problems/0416.分割等和子集.md +++ b/problems/0416.分割等和子集.md @@ -218,8 +218,12 @@ class Solution { for(int i = 0; i < n; i++) { for(int j = target; j >= nums[i]; j--) { //物品 i 的重量是 nums[i],其价值也是 nums[i] - dp[j] = Math.max(dp[j], dp[j-nums[i]] + nums[i]); + dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]); } + + //剪枝一下,每一次完成內層的for-loop,立即檢查是否dp[target] == target,優化時間複雜度(26ms -> 20ms) + if(dp[target] == target) + return true; } return dp[target] == target; } @@ -294,6 +298,61 @@ false true false false false true true false false false false false false true false false false true true false false false false true false true false false false true true false false false true true ``` +二維數組整數版本 +```Java +class Solution { + public boolean canPartition(int[] nums) { + //using 2-D DP array. + int len = nums.length; + //check edge cases; + if(len == 0) + return false; + + int sum = 0; + for (int num : nums) + sum += num; + //we only deal with even numbers. If sum is odd, return false; + if(sum % 2 == 1) + return false; + + int target = sum / 2; + int[][] dp = new int[nums.length][target + 1]; + + // for(int j = 0; j <= target; j++){ + // if(j < nums[0]) + // dp[0][j] = 0; + // else + // dp[0][j] = nums[0]; + // } + + //initialize dp array + for(int j = nums[0]; j <= target; j++){ + dp[0][j] = nums[0]; + } + + for(int i = 1; i < len; i++){ + for(int j = 0; j <= target; j++){ + if (j < nums[i]) + dp[i][j] = dp[i - 1][j]; + else + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]); + } + } + + //print out DP array + // for(int x : dp){ + // System.out.print(x + ","); + // } + // System.out.print(" "+i+" row"+"\n"); + return dp[len - 1][target] == target; + } +} +//dp数组的打印结果 for test case 1. +0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +0, 1, 1, 1, 1, 5, 6, 6, 6, 6, 6, 6, +0, 1, 1, 1, 1, 5, 6, 6, 6, 6, 6, 11, +0, 1, 1, 1, 1, 5, 6, 6, 6, 6, 10, 11, +``` ### Python: ```python From 45863868708ba1c3260060fb5afa854fa6874184 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Fri, 26 May 2023 21:03:46 -0500 Subject: [PATCH 064/122] =?UTF-8?q?Update=200131.=E5=88=86=E5=89=B2?= =?UTF-8?q?=E5=9B=9E=E6=96=87=E4=B8=B2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0131.分割回文串.md | 143 +++++++++++++++++++++++------------- 1 file changed, 91 insertions(+), 52 deletions(-) diff --git a/problems/0131.分割回文串.md b/problems/0131.分割回文串.md index 30bba455..53f43eea 100644 --- a/problems/0131.分割回文串.md +++ b/problems/0131.分割回文串.md @@ -349,12 +349,9 @@ class Solution { ``` ## Python -**回溯+正反序判断回文串** +回溯 基本版 ```python class Solution: - def __init__(self): - self.paths = [] - self.path = [] def partition(self, s: str) -> List[List[str]]: ''' @@ -363,52 +360,14 @@ class Solution: 当切割线迭代至字符串末尾,说明找到一种方法 类似组合问题,为了不重复切割同一位置,需要start_index来做标记下一轮递归的起始位置(切割线) ''' - self.path.clear() - self.paths.clear() - self.backtracking(s, 0) - return self.paths + result = [] + self.backtracking(s, 0, [], result) + return result - def backtracking(self, s: str, start_index: int) -> None: + def backtracking(self, s, start_index, path, result ): # Base Case - if start_index >= len(s): - self.paths.append(self.path[:]) - return - - # 单层递归逻辑 - for i in range(start_index, len(s)): - # 此次比其他组合题目多了一步判断: - # 判断被截取的这一段子串([start_index, i])是否为回文串 - temp = s[start_index:i+1] - if temp == temp[::-1]: # 若反序和正序相同,意味着这是回文串 - self.path.append(temp) - self.backtracking(s, i+1) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串 - self.path.pop() - else: - continue -``` -**回溯+函数判断回文串** -```python -class Solution: - def __init__(self): - self.paths = [] - self.path = [] - - def partition(self, s: str) -> List[List[str]]: - ''' - 递归用于纵向遍历 - for循环用于横向遍历 - 当切割线迭代至字符串末尾,说明找到一种方法 - 类似组合问题,为了不重复切割同一位置,需要start_index来做标记下一轮递归的起始位置(切割线) - ''' - self.path.clear() - self.paths.clear() - self.backtracking(s, 0) - return self.paths - - def backtracking(self, s: str, start_index: int) -> None: - # Base Case - if start_index >= len(s): - self.paths.append(self.path[:]) + if start_index == len(s): + result.append(path[:]) return # 单层递归逻辑 @@ -416,9 +375,9 @@ class Solution: # 此次比其他组合题目多了一步判断: # 判断被截取的这一段子串([start_index, i])是否为回文串 if self.is_palindrome(s, start_index, i): - self.path.append(s[start_index:i+1]) - self.backtracking(s, i+1) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串 - self.path.pop() # 回溯 + path.append(s[start_index:i+1]) + self.backtracking(s, i+1, path, result) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串 + path.pop() # 回溯 else: continue @@ -430,9 +389,89 @@ class Solution: return False i += 1 j -= 1 - return True + return True ``` +回溯+优化判定回文函数 +```python +class Solution: + def partition(self, s: str) -> List[List[str]]: + result = [] + self.backtracking(s, 0, [], result) + return result + + def backtracking(self, s, start_index, path, result ): + # Base Case + if start_index == len(s): + result.append(path[:]) + return + + # 单层递归逻辑 + for i in range(start_index, len(s)): + # 若反序和正序相同,意味着这是回文串 + if s[start_index: i + 1] == s[start_index: i + 1][::-1]: + path.append(s[start_index:i+1]) + self.backtracking(s, i+1, path, result) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串 + path.pop() # 回溯 + else: + continue +``` +回溯+高效判断回文子串 +```python +class Solution: + def partition(self, s: str) -> List[List[str]]: + result = [] + isPalindrome = [[False] * len(s) for _ in range(len(s))] # 初始化isPalindrome矩阵 + self.computePalindrome(s, isPalindrome) + self.backtracking(s, 0, [], result, isPalindrome) + return result + + def backtracking(self, s, startIndex, path, result, isPalindrome): + if startIndex >= len(s): + result.append(path[:]) + return + + for i in range(startIndex, len(s)): + if isPalindrome[startIndex][i]: # 是回文子串 + substring = s[startIndex:i + 1] + path.append(substring) + self.backtracking(s, i + 1, path, result, isPalindrome) # 寻找i+1为起始位置的子串 + path.pop() # 回溯过程,弹出本次已经填在的子串 + + def computePalindrome(self, s, isPalindrome): + for i in range(len(s) - 1, -1, -1): # 需要倒序计算,保证在i行时,i+1行已经计算好了 + for j in range(i, len(s)): + if j == i: + isPalindrome[i][j] = True + elif j - i == 1: + isPalindrome[i][j] = (s[i] == s[j]) + else: + isPalindrome[i][j] = (s[i] == s[j] and isPalindrome[i+1][j-1]) +``` +回溯+使用all函数判断回文子串 +```python +class Solution: + def partition(self, s: str) -> List[List[str]]: + result = [] + self.partition_helper(s, 0, [], result) + return result + + def partition_helper(self, s, start_index, path, result): + if start_index == len(s): + result.append(path[:]) + return + + for i in range(start_index + 1, len(s) + 1): + sub = s[start_index:i] + if self.isPalindrome(sub): + path.append(sub) + self.partition_helper(s, i, path, result) + path.pop() + + def isPalindrome(self, s): + return all(s[i] == s[len(s) - 1 - i] for i in range(len(s) // 2)) + +``` ## Go ```go var ( From 78445d75573bd7d45e42a52cb1388a5f61449a73 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Fri, 26 May 2023 21:06:59 -0500 Subject: [PATCH 065/122] =?UTF-8?q?Update=200131.=E5=88=86=E5=89=B2?= =?UTF-8?q?=E5=9B=9E=E6=96=87=E4=B8=B2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0131.分割回文串.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/problems/0131.分割回文串.md b/problems/0131.分割回文串.md index 53f43eea..a797a0b5 100644 --- a/problems/0131.分割回文串.md +++ b/problems/0131.分割回文串.md @@ -378,8 +378,7 @@ class Solution: path.append(s[start_index:i+1]) self.backtracking(s, i+1, path, result) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串 path.pop() # 回溯 - else: - continue + def is_palindrome(self, s: str, start: int, end: int) -> bool: i: int = start @@ -413,8 +412,7 @@ class Solution: path.append(s[start_index:i+1]) self.backtracking(s, i+1, path, result) # 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串 path.pop() # 回溯 - else: - continue + ``` 回溯+高效判断回文子串 ```python From 51c89762f6c9643f76da1ea7fb516ca204fd8634 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 27 May 2023 00:51:28 -0500 Subject: [PATCH 066/122] =?UTF-8?q?Update=200093.=E5=A4=8D=E5=8E=9FIP?= =?UTF-8?q?=E5=9C=B0=E5=9D=80.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0093.复原IP地址.md | 146 +++++++++++++++--------------------- 1 file changed, 62 insertions(+), 84 deletions(-) diff --git a/problems/0093.复原IP地址.md b/problems/0093.复原IP地址.md index 9d4d5918..81192ce5 100644 --- a/problems/0093.复原IP地址.md +++ b/problems/0093.复原IP地址.md @@ -360,104 +360,82 @@ class Solution { ## python -python2: -```python -class Solution(object): - def restoreIpAddresses(self, s): - """ - :type s: str - :rtype: List[str] - """ - ans = [] - path = [] - def backtrack(path, startIndex): - if len(s) > 12: return [] - if len(path) == 4: - if startIndex == len(s): - ans.append(".".join(path[:])) - return - for i in range(startIndex+1, min(startIndex+4, len(s)+1)): # 剪枝 - string = s[startIndex:i] - if not 0 <= int(string) <= 255: - continue - if not string == "0" and not string.lstrip('0') == string: - continue - path.append(string) - backtrack(path, i) - path.pop() - - backtrack([], 0) - return ans -``` - -python3: +回溯(版本一) ```python class Solution: - def __init__(self): - self.result = [] - def restoreIpAddresses(self, s: str) -> List[str]: - ''' - 本质切割问题使用回溯搜索法,本题只能切割三次,所以纵向递归总共四层 - 因为不能重复分割,所以需要start_index来记录下一层递归分割的起始位置 - 添加变量point_num来记录逗号的数量[0,3] - ''' - self.result.clear() - if len(s) > 12: return [] - self.backtracking(s, 0, 0) - return self.result + result = [] + self.backtracking(s, 0, 0, "", result) + return result - def backtracking(self, s: str, start_index: int, point_num: int) -> None: - # Base Case - if point_num == 3: - if self.is_valid(s, start_index, len(s)-1): - self.result.append(s[:]) + def backtracking(self, s, start_index, point_num, current, result): + if point_num == 3: # 逗点数量为3时,分隔结束 + if self.is_valid(s, start_index, len(s) - 1): # 判断第四段子字符串是否合法 + current += s[start_index:] # 添加最后一段子字符串 + result.append(current) return - # 单层递归逻辑 - for i in range(start_index, len(s)): - # [start_index, i]就是被截取的子串 - if self.is_valid(s, start_index, i): - s = s[:i+1] + '.' + s[i+1:] - self.backtracking(s, i+2, point_num+1) # 在填入.后,下一子串起始后移2位 - s = s[:i+1] + s[i+2:] # 回溯 - else: - # 若当前被截取的子串大于255或者大于三位数,直接结束本层循环 - break - - def is_valid(self, s: str, start: int, end: int) -> bool: - if start > end: return False - # 若数字是0开头,不合法 - if s[start] == '0' and start != end: - return False - if not 0 <= int(s[start:end+1]) <= 255: - return False - return True -``` -python3; 简单拼接版本(类似Leetcode131写法): + for i in range(start_index, len(s)): + if self.is_valid(s, start_index, i): # 判断 [start_index, i] 这个区间的子串是否合法 + sub = s[start_index:i + 1] + self.backtracking(s, i + 1, point_num + 1, current + sub + '.', result) + else: + break + + def is_valid(self, s, start, end): + if start > end: + return False + if s[start] == '0' and start != end: # 0开头的数字不合法 + return False + num = 0 + for i in range(start, end + 1): + if not s[i].isdigit(): # 遇到非数字字符不合法 + return False + num = num * 10 + int(s[i]) + if num > 255: # 如果大于255了不合法 + return False + return True + +``` +回溯(版本二) + ```python -class Solution: +class Solution: def restoreIpAddresses(self, s: str) -> List[str]: - global results, path results = [] - path = [] - self.backtracking(s,0) + self.backtracking(s, 0, [], results) return results - def backtracking(self,s,index): - global results,path - if index == len(s) and len(path)==4: - results.append('.'.join(path)) # 在连接时需要中间间隔符号的话就在''中间写上对应的间隔符 + def backtracking(self, s, index, path, results): + if index == len(s) and len(path) == 4: + results.append('.'.join(path)) return - for i in range(index,len(s)): - if len(path)>3: break # 剪枝 - temp = s[index:i+1] - if (int(temp)<256 and int(temp)>0 and temp[0]!='0') or (temp=='0'): - path.append(temp) - self.backtracking(s,i+1) - path.pop() + + if len(path) > 4: # 剪枝 + return + + for i in range(index, min(index + 3, len(s))): + if self.is_valid(s, index, i): + sub = s[index:i+1] + path.append(sub) + self.backtracking(s, i+1, path, results) + path.pop() + + def is_valid(self, s, start, end): + if start > end: + return False + if s[start] == '0' and start != end: # 0开头的数字不合法 + return False + num = int(s[start:end+1]) + return 0 <= num <= 255 + + + + ``` + + ## Go ```go From d45c141cc676cf0ef55dccd85705f3eb5c8323a7 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 27 May 2023 18:38:57 -0500 Subject: [PATCH 067/122] =?UTF-8?q?Update=200078.=E5=AD=90=E9=9B=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0078.子集.md | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/problems/0078.子集.md b/problems/0078.子集.md index f26d0821..ecc202b7 100644 --- a/problems/0078.子集.md +++ b/problems/0078.子集.md @@ -206,28 +206,20 @@ class Solution { ## Python ```python class Solution: - def __init__(self): - self.path: List[int] = [] - self.paths: List[List[int]] = [] + def subsets(self, nums): + result = [] + path = [] + self.backtracking(nums, 0, path, result) + return result - def subsets(self, nums: List[int]) -> List[List[int]]: - self.paths.clear() - self.path.clear() - self.backtracking(nums, 0) - return self.paths - - def backtracking(self, nums: List[int], start_index: int) -> None: - # 收集子集,要先于终止判断 - self.paths.append(self.path[:]) - # Base Case - if start_index == len(nums): - return - - # 单层递归逻辑 - for i in range(start_index, len(nums)): - self.path.append(nums[i]) - self.backtracking(nums, i+1) - self.path.pop() # 回溯 + def backtracking(self, nums, startIndex, path, result): + result.append(path[:]) # 收集子集,要放在终止添加的上面,否则会漏掉自己 + # if startIndex >= len(nums): # 终止条件可以不加 + # return + for i in range(startIndex, len(nums)): + path.append(nums[i]) + self.backtracking(nums, i + 1, path, result) + path.pop() ``` ## Go From 5801ae752325eddc69508dd3635fadc6b74abf69 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 27 May 2023 18:53:28 -0500 Subject: [PATCH 068/122] =?UTF-8?q?Update=200090.=E5=AD=90=E9=9B=86II.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0090.子集II.md | 136 ++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 69 deletions(-) diff --git a/problems/0090.子集II.md b/problems/0090.子集II.md index 1a9f8fda..0df0600e 100644 --- a/problems/0090.子集II.md +++ b/problems/0090.子集II.md @@ -235,86 +235,84 @@ class Solution { } ``` -### Python -```python -class Solution: - def __init__(self): - self.paths = [] - self.path = [] - def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: - nums.sort() - self.backtracking(nums, 0) - return self.paths - - def backtracking(self, nums: List[int], start_index: int) -> None: - # ps.空集合仍符合要求 - self.paths.append(self.path[:]) - # Base Case - if start_index == len(nums): - return - - # 单层递归逻辑 - for i in range(start_index, len(nums)): - if i > start_index and nums[i] == nums[i-1]: - # 当前后元素值相同时,跳入下一个循环,去重 - continue - self.path.append(nums[i]) - self.backtracking(nums, i+1) - self.path.pop() -``` #### Python3 -不使用used数组 +回溯 利用used数组去重 ```python class Solution: - def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: - res = [] - path = [] - nums.sort() # 去重需要先对数组进行排序 - - def backtracking(nums, startIndex): - # 终止条件 - res.append(path[:]) - if startIndex == len(nums): - return - - # for循环 - for i in range(startIndex, len(nums)): - # 数层去重 - if i > startIndex and nums[i] == nums[i-1]: # 去重 - continue - path.append(nums[i]) - backtracking(nums, i+1) - path.pop() - - backtracking(nums, 0) - return res -``` - -使用used数组 -```python -class Solution: - def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: + def subsetsWithDup(self, nums): result = [] path = [] - nums.sort() - used = [0] * len(nums) - def backtrack(nums, startIdx): - result.append(path[:]) - for i in range(startIdx, len(nums)): - if i > startIdx and nums[i] == nums[i-1] and used[i-1] == 0: - continue - used[i] = 1 - path.append(nums[i]) - backtrack(nums, i+1) - path.pop() - used[i] = 0 - backtrack(nums, 0) + used = [False] * len(nums) + nums.sort() # 去重需要排序 + self.backtracking(nums, 0, used, path, result) return result + + def backtracking(self, nums, startIndex, used, path, result): + result.append(path[:]) # 收集子集 + for i in range(startIndex, len(nums)): + # used[i - 1] == True,说明同一树枝 nums[i - 1] 使用过 + # used[i - 1] == False,说明同一树层 nums[i - 1] 使用过 + # 而我们要对同一树层使用过的元素进行跳过 + if i > 0 and nums[i] == nums[i - 1] and not used[i - 1]: + continue + path.append(nums[i]) + used[i] = True + self.backtracking(nums, i + 1, used, path, result) + used[i] = False + path.pop() + ``` +回溯 利用集合去重 + +```python +class Solution: + def subsetsWithDup(self, nums): + result = [] + path = [] + nums.sort() # 去重需要排序 + self.backtracking(nums, 0, path, result) + return result + + def backtracking(self, nums, startIndex, path, result): + result.append(path[:]) # 收集子集 + uset = set() + for i in range(startIndex, len(nums)): + if nums[i] in uset: + continue + uset.add(nums[i]) + path.append(nums[i]) + self.backtracking(nums, i + 1, path, result) + path.pop() + +``` + +回溯 利用递归的时候下一个startIndex是i+1而不是0去重 + +```python +class Solution: + def subsetsWithDup(self, nums): + result = [] + path = [] + nums.sort() # 去重需要排序 + self.backtracking(nums, 0, path, result) + return result + + def backtracking(self, nums, startIndex, path, result): + result.append(path[:]) # 收集子集 + for i in range(startIndex, len(nums)): + # 而我们要对同一树层使用过的元素进行跳过 + if i > startIndex and nums[i] == nums[i - 1]: + continue + path.append(nums[i]) + self.backtracking(nums, i + 1, path, result) + path.pop() + + +``` ### Go ```Go From eb72f2637195f8c50bff7660ba90ddf217654840 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 27 May 2023 19:17:26 -0500 Subject: [PATCH 069/122] =?UTF-8?q?Update=200491.=E9=80=92=E5=A2=9E?= =?UTF-8?q?=E5=AD=90=E5=BA=8F=E5=88=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0491.递增子序列.md | 98 ++++++++++++++----------------------- 1 file changed, 36 insertions(+), 62 deletions(-) diff --git a/problems/0491.递增子序列.md b/problems/0491.递增子序列.md index 436dbf01..56a70e8b 100644 --- a/problems/0491.递增子序列.md +++ b/problems/0491.递增子序列.md @@ -267,79 +267,53 @@ class Solution { ### Python python3 -**回溯** +回溯 利用set去重 ```python class Solution: - def __init__(self): - self.paths = [] - self.path = [] + def findSubsequences(self, nums): + result = [] + self.backtracking(nums, 0, [], result) + return result - def findSubsequences(self, nums: List[int]) -> List[List[int]]: - ''' - 本题求自增子序列,所以不能改变原数组顺序 - ''' - self.backtracking(nums, 0) - return self.paths - - def backtracking(self, nums: List[int], start_index: int): - # 收集结果,同78.子集,仍要置于终止条件之前 - if len(self.path) >= 2: - # 本题要求所有的节点 - self.paths.append(self.path[:]) + def backtracking(self, nums, startIndex, path, result): + if len(path) > 1: + result.append(path[:]) # 将当前路径的副本加入结果集 - # Base Case(可忽略) - if start_index == len(nums): - return + used = set() # 使用集合来进行去重操作 + for i in range(startIndex, len(nums)): + if path and nums[i] < path[-1]: + continue # 如果当前元素小于上一个元素,则跳过当前元素 + + if nums[i] in used: + continue # 如果当前元素已经使用过,则跳过当前元素 + + used.add(nums[i]) # 标记当前元素已经使用过 + self.backtracking(nums, i + 1, path + [nums[i]], result) - # 单层递归逻辑 - # 深度遍历中每一层都会有一个全新的usage_list用于记录本层元素是否重复使用 - usage_list = set() - # 同层横向遍历 - for i in range(start_index, len(nums)): - # 若当前元素值小于前一个时(非递增)或者曾用过,跳入下一循环 - if (self.path and nums[i] < self.path[-1]) or nums[i] in usage_list: - continue - usage_list.add(nums[i]) - self.path.append(nums[i]) - self.backtracking(nums, i+1) - self.path.pop() ``` -**回溯+哈希表去重** +回溯 利用哈希表去重 ```python class Solution: - def __init__(self): - self.paths = [] - self.path = [] + def findSubsequences(self, nums): + result = [] + path = [] + self.backtracking(nums, 0, path, result) + return result - def findSubsequences(self, nums: List[int]) -> List[List[int]]: - ''' - 本题求自增子序列,所以不能改变原数组顺序 - ''' - self.backtracking(nums, 0) - return self.paths - - def backtracking(self, nums: List[int], start_index: int): - # 收集结果,同78.子集,仍要置于终止条件之前 - if len(self.path) >= 2: - # 本题要求所有的节点 - self.paths.append(self.path[:]) + def backtracking(self, nums, startIndex, path, result): + if len(path) > 1: + result.append(path[:]) # 注意要使用切片将当前路径的副本加入结果集 - # Base Case(可忽略) - if start_index == len(nums): - return + used = [0] * 201 # 使用数组来进行去重操作,题目说数值范围[-100, 100] + for i in range(startIndex, len(nums)): + if (path and nums[i] < path[-1]) or used[nums[i] + 100] == 1: + continue # 如果当前元素小于上一个元素,或者已经使用过当前元素,则跳过当前元素 + + used[nums[i] + 100] = 1 # 标记当前元素已经使用过 + path.append(nums[i]) # 将当前元素加入当前递增子序列 + self.backtracking(nums, i + 1, path, result) + path.pop() - # 单层递归逻辑 - # 深度遍历中每一层都会有一个全新的usage_list用于记录本层元素是否重复使用 - usage_list = [False] * 201 # 使用列表去重,题中取值范围[-100, 100] - # 同层横向遍历 - for i in range(start_index, len(nums)): - # 若当前元素值小于前一个时(非递增)或者曾用过,跳入下一循环 - if (self.path and nums[i] < self.path[-1]) or usage_list[nums[i]+100] == True: - continue - usage_list[nums[i]+100] = True - self.path.append(nums[i]) - self.backtracking(nums, i+1) - self.path.pop() ``` ### Go From 7e3fc8ea0d59a9a2c0e1765b62cc6e386cef5fdf Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 27 May 2023 19:17:50 -0500 Subject: [PATCH 070/122] =?UTF-8?q?Update=200491.=E9=80=92=E5=A2=9E?= =?UTF-8?q?=E5=AD=90=E5=BA=8F=E5=88=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0491.递增子序列.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0491.递增子序列.md b/problems/0491.递增子序列.md index 56a70e8b..a805a262 100644 --- a/problems/0491.递增子序列.md +++ b/problems/0491.递增子序列.md @@ -266,7 +266,7 @@ class Solution { ### Python -python3 + 回溯 利用set去重 ```python class Solution: From a6c95b78e80f754dc901747046e06008710a639f Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 27 May 2023 19:21:17 -0500 Subject: [PATCH 071/122] =?UTF-8?q?Update=200491.=E9=80=92=E5=A2=9E?= =?UTF-8?q?=E5=AD=90=E5=BA=8F=E5=88=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0491.递增子序列.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/problems/0491.递增子序列.md b/problems/0491.递增子序列.md index a805a262..50cb9918 100644 --- a/problems/0491.递增子序列.md +++ b/problems/0491.递增子序列.md @@ -272,23 +272,24 @@ class Solution { class Solution: def findSubsequences(self, nums): result = [] - self.backtracking(nums, 0, [], result) + path = [] + self.backtracking(nums, 0, path, result) return result - + def backtracking(self, nums, startIndex, path, result): if len(path) > 1: - result.append(path[:]) # 将当前路径的副本加入结果集 + result.append(path[:]) # 注意要使用切片将当前路径的副本加入结果集 + # 注意这里不要加return,要取树上的节点 - used = set() # 使用集合来进行去重操作 + uset = set() # 使用集合对本层元素进行去重 for i in range(startIndex, len(nums)): - if path and nums[i] < path[-1]: - continue # 如果当前元素小于上一个元素,则跳过当前元素 + if (path and nums[i] < path[-1]) or nums[i] in uset: + continue - if nums[i] in used: - continue # 如果当前元素已经使用过,则跳过当前元素 - - used.add(nums[i]) # 标记当前元素已经使用过 - self.backtracking(nums, i + 1, path + [nums[i]], result) + uset.add(nums[i]) # 记录这个元素在本层用过了,本层后面不能再用了 + path.append(nums[i]) + self.backtracking(nums, i + 1, path, result) + path.pop() ``` 回溯 利用哈希表去重 @@ -314,6 +315,7 @@ class Solution: self.backtracking(nums, i + 1, path, result) path.pop() + ``` ### Go From b033a08c13547a43f3ac4a873f65151987569e6a Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 27 May 2023 20:53:39 -0500 Subject: [PATCH 072/122] =?UTF-8?q?Update=200046.=E5=85=A8=E6=8E=92?= =?UTF-8?q?=E5=88=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0046.全排列.md | 71 +++++++++-------------------------------- 1 file changed, 15 insertions(+), 56 deletions(-) diff --git a/problems/0046.全排列.md b/problems/0046.全排列.md index e08aec94..029a98e3 100644 --- a/problems/0046.全排列.md +++ b/problems/0046.全排列.md @@ -213,68 +213,27 @@ class Solution { ``` ### Python -**回溯** +回溯 使用used ```python class Solution: - def __init__(self): - self.path = [] - self.paths = [] + def permute(self, nums): + result = [] + self.backtracking(nums, [], [False] * len(nums), result) + return result - def permute(self, nums: List[int]) -> List[List[int]]: - ''' - 因为本题排列是有序的,这意味着同一层的元素可以重复使用,但同一树枝上不能重复使用(usage_list) - 所以处理排列问题每层都需要从头搜索,故不再使用start_index - ''' - usage_list = [False] * len(nums) - self.backtracking(nums, usage_list) - return self.paths - - def backtracking(self, nums: List[int], usage_list: List[bool]) -> None: - # Base Case本题求叶子节点 - if len(self.path) == len(nums): - self.paths.append(self.path[:]) + def backtracking(self, nums, path, used, result): + if len(path) == len(nums): + result.append(path[:]) return - - # 单层递归逻辑 - for i in range(0, len(nums)): # 从头开始搜索 - # 若遇到self.path里已收录的元素,跳过 - if usage_list[i] == True: + for i in range(len(nums)): + if used[i]: continue - usage_list[i] = True - self.path.append(nums[i]) - self.backtracking(nums, usage_list) # 纵向传递使用信息,去重 - self.path.pop() - usage_list[i] = False -``` -**回溯+丢掉usage_list** -```python -class Solution: - def __init__(self): - self.path = [] - self.paths = [] + used[i] = True + path.append(nums[i]) + self.backtracking(nums, path, used, result) + path.pop() + used[i] = False - def permute(self, nums: List[int]) -> List[List[int]]: - ''' - 因为本题排列是有序的,这意味着同一层的元素可以重复使用,但同一树枝上不能重复使用 - 所以处理排列问题每层都需要从头搜索,故不再使用start_index - ''' - self.backtracking(nums) - return self.paths - - def backtracking(self, nums: List[int]) -> None: - # Base Case本题求叶子节点 - if len(self.path) == len(nums): - self.paths.append(self.path[:]) - return - - # 单层递归逻辑 - for i in range(0, len(nums)): # 从头开始搜索 - # 若遇到self.path里已收录的元素,跳过 - if nums[i] in self.path: - continue - self.path.append(nums[i]) - self.backtracking(nums) - self.path.pop() ``` ### Go From fd2608a38c99b2ff98130112c9c3a555b1be362d Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 27 May 2023 21:20:59 -0500 Subject: [PATCH 073/122] =?UTF-8?q?Update=200047.=E5=85=A8=E6=8E=92?= =?UTF-8?q?=E5=88=97II.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0047.全排列II.md | 41 ++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/problems/0047.全排列II.md b/problems/0047.全排列II.md index b4f7a4d8..de9b2bb4 100644 --- a/problems/0047.全排列II.md +++ b/problems/0047.全排列II.md @@ -208,28 +208,25 @@ class Solution { ```python class Solution: - def permuteUnique(self, nums: List[int]) -> List[List[int]]: - # res用来存放结果 - if not nums: return [] - res = [] - used = [0] * len(nums) - def backtracking(nums, used, path): - # 终止条件 - if len(path) == len(nums): - res.append(path.copy()) - return - for i in range(len(nums)): - if not used[i]: - if i>0 and nums[i] == nums[i-1] and not used[i-1]: - continue - used[i] = 1 - path.append(nums[i]) - backtracking(nums, used, path) - path.pop() - used[i] = 0 - # 记得给nums排序 - backtracking(sorted(nums),used,[]) - return res + def permuteUnique(self, nums): + nums.sort() # 排序 + result = [] + self.backtracking(nums, [], [False] * len(nums), result) + return result + + def backtracking(self, nums, path, used, result): + if len(path) == len(nums): + result.append(path[:]) + return + for i in range(len(nums)): + if (i > 0 and nums[i] == nums[i - 1] and not used[i - 1]) or used[i]: + continue + used[i] = True + path.append(nums[i]) + self.backtracking(nums, path, used, result) + path.pop() + used[i] = False + ``` ### Go From 19ecde0984c6bc4e383b2df72a47ff4032a77ca4 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 27 May 2023 21:49:43 -0500 Subject: [PATCH 074/122] =?UTF-8?q?Update=20=E5=9B=9E=E6=BA=AF=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E5=8E=BB=E9=87=8D=E9=97=AE=E9=A2=98=E7=9A=84=E5=8F=A6?= =?UTF-8?q?=E4=B8=80=E7=A7=8D=E5=86=99=E6=B3=95.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/回溯算法去重问题的另一种写法.md | 102 ++++++++++++----------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/problems/回溯算法去重问题的另一种写法.md b/problems/回溯算法去重问题的另一种写法.md index f39c3c74..4e09b925 100644 --- a/problems/回溯算法去重问题的另一种写法.md +++ b/problems/回溯算法去重问题的另一种写法.md @@ -356,76 +356,80 @@ Python: ```python class Solution: - def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: - res = [] - nums.sort() - def backtracking(start, path): - res.append(path) - uset = set() - for i in range(start, len(nums)): - if nums[i] not in uset: - backtracking(i + 1, path + [nums[i]]) - uset.add(nums[i]) + def subsetsWithDup(self, nums): + nums.sort() # 去重需要排序 + result = [] + self.backtracking(nums, 0, [], result) + return result + + def backtracking(self, nums, startIndex, path, result): + result.append(path[:]) + used = set() + for i in range(startIndex, len(nums)): + if nums[i] in used: + continue + used.add(nums[i]) + path.append(nums[i]) + self.backtracking(nums, i + 1, path, result) + path.pop() - backtracking(0, []) - return res ``` **40. 组合总和 II** ```python class Solution: - def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: - - res = [] + def combinationSum2(self, candidates, target): candidates.sort() + result = [] + self.backtracking(candidates, target, 0, 0, [], result) + return result - def backtracking(start, path): - if sum(path) == target: - res.append(path) - elif sum(path) < target: - used = set() - for i in range(start, len(candidates)): - if candidates[i] in used: - continue - else: - used.add(candidates[i]) - backtracking(i + 1, path + [candidates[i]]) - - backtracking(0, []) + def backtracking(self, candidates, target, sum, startIndex, path, result): + if sum == target: + result.append(path[:]) + return + used = set() + for i in range(startIndex, len(candidates)): + if sum + candidates[i] > target: + break + if candidates[i] in used: + continue + used.add(candidates[i]) + sum += candidates[i] + path.append(candidates[i]) + self.backtracking(candidates, target, sum, i + 1, path, result) + sum -= candidates[i] + path.pop() - return res ``` **47. 全排列 II** ```python class Solution: - def permuteUnique(self, nums: List[int]) -> List[List[int]]: - path = [] - res = [] - used = [False]*len(nums) + def permuteUnique(self, nums): + nums.sort() # 排序 + result = [] + self.backtracking(nums, [False] * len(nums), [], result) + return result - def backtracking(): - if len(path) == len(nums): - res.append(path.copy()) - - deduplicate = set() - for i, num in enumerate(nums): - if used[i] == True: - continue - if num in deduplicate: - continue + def backtracking(self, nums, used, path, result): + if len(path) == len(nums): + result.append(path[:]) + return + used_set = set() + for i in range(len(nums)): + if nums[i] in used_set: + continue + if not used[i]: + used_set.add(nums[i]) used[i] = True path.append(nums[i]) - backtracking() - used[i] = False + self.backtracking(nums, used, path, result) path.pop() - deduplicate.add(num) - - backtracking() + used[i] = False - return res ``` JavaScript: From 498395d07a6363198a99a530555c768432f1148d Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 27 May 2023 22:18:11 -0500 Subject: [PATCH 075/122] =?UTF-8?q?Update=200332.=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E5=AE=89=E6=8E=92=E8=A1=8C=E7=A8=8B.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0332.重新安排行程.md | 126 ++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 51 deletions(-) diff --git a/problems/0332.重新安排行程.md b/problems/0332.重新安排行程.md index 95e7d3ed..fa6414e9 100644 --- a/problems/0332.重新安排行程.md +++ b/problems/0332.重新安排行程.md @@ -347,64 +347,88 @@ class Solution { ``` ### python +回溯 使用used数组 ```python class Solution: def findItinerary(self, tickets: List[List[str]]) -> List[str]: - # defaultdic(list) 是为了方便直接append - tickets_dict = defaultdict(list) - for item in tickets: - tickets_dict[item[0]].append(item[1]) - # 给每一个机场的到达机场排序,小的在前面,在回溯里首先被pop(0)出去 - # 这样最先找的的path就是排序最小的答案,直接返回 - for airport in tickets_dict: tickets_dict[airport].sort() - ''' - tickets_dict里面的内容是这样的 - {'JFK': ['ATL', 'SFO'], 'SFO': ['ATL'], 'ATL': ['JFK', 'SFO']}) - ''' - path = ["JFK"] - def backtracking(start_point): - # 终止条件 - if len(path) == len(tickets) + 1: - return True - for _ in tickets_dict[start_point]: - #必须及时删除,避免出现死循环 - end_point = tickets_dict[start_point].pop(0) - path.append(end_point) - # 只要找到一个就可以返回了 - if backtracking(end_point): - return True - path.pop() - tickets_dict[start_point].append(end_point) - - backtracking("JFK") - return path -``` - -python - 使用used数组 - 神似之前几题写法 - -```python -class Solution: - def findItinerary(self, tickets: List[List[str]]) -> List[str]: - global used,path,results - used = [0]*len(tickets) + tickets.sort() # 先排序,这样一旦找到第一个可行路径,一定是字母排序最小的 + used = [0] * len(tickets) path = ['JFK'] results = [] - tickets.sort() # 先排序,这样一旦找到第一个可行路径,一定是字母排序最小的 - self.backtracking(tickets,'JFK') + self.backtracking(tickets, used, path, 'JFK', results) return results[0] - def backtracking(self,tickets,cur): - if sum(used) == len(tickets): - results.append(path[:]) - return True # 只要找到就返回 - for i in range(len(tickets)): - if tickets[i][0]==cur and used[i]==0: - used[i]=1 - path.append(tickets[i][1]) - state = self.backtracking(tickets,tickets[i][1]) - path.pop() - used[i]=0 - if state: return True # 只要找到就返回,不继续搜索了 + + def backtracking(self, tickets, used, path, cur, results): + if len(path) == len(tickets) + 1: # 终止条件:路径长度等于机票数量+1 + results.append(path[:]) # 将当前路径添加到结果列表 + return True + + for i, ticket in enumerate(tickets): # 遍历机票列表 + if ticket[0] == cur and used[i] == 0: # 找到起始机场为cur且未使用过的机票 + used[i] = 1 # 标记该机票为已使用 + path.append(ticket[1]) # 将到达机场添加到路径中 + state = self.backtracking(tickets, used, path, ticket[1], results) # 递归搜索 + path.pop() # 回溯,移除最后添加的到达机场 + used[i] = 0 # 标记该机票为未使用 + if state: + return True # 只要找到一个可行路径就返回,不继续搜索 + +``` +回溯 使用字典 +```python +from collections import defaultdict + +class Solution: + def findItinerary(self, tickets: List[List[str]]) -> List[str]: + targets = defaultdict(list) # 构建机场字典 + for ticket in tickets: + targets[ticket[0]].append(ticket[1]) + for airport in targets: + targets[airport].sort() # 对目的地列表进行排序 + + path = ["JFK"] # 起始机场为"JFK" + self.backtracking(targets, path, len(tickets)) + return path + + def backtracking(self, targets, path, ticketNum): + if len(path) == ticketNum + 1: + return True # 找到有效行程 + + airport = path[-1] # 当前机场 + destinations = targets[airport] # 当前机场可以到达的目的地列表 + for i, dest in enumerate(destinations): + targets[airport].pop(i) # 标记已使用的机票 + path.append(dest) # 添加目的地到路径 + if self.backtracking(targets, path, ticketNum): + return True # 找到有效行程 + targets[airport].insert(i, dest) # 回溯,恢复机票 + path.pop() # 移除目的地 + return False # 没有找到有效行程 + +``` +回溯 使用字典 逆序 +```python +from collections import defaultdict + +class Solution: + def findItinerary(self, tickets): + targets = defaultdict(list) # 创建默认字典,用于存储机场映射关系 + for ticket in tickets: + targets[ticket[0]].append(ticket[1]) # 将机票输入到字典中 + + for key in targets: + targets[key].sort(reverse=True) # 对到达机场列表进行字母逆序排序 + + result = [] + self.backtracking("JFK", targets, result) # 调用回溯函数开始搜索路径 + return result[::-1] # 返回逆序的行程路径 + + def backtracking(self, airport, targets, result): + while targets[airport]: # 当机场还有可到达的机场时 + next_airport = targets[airport].pop() # 弹出下一个机场 + self.backtracking(next_airport, targets, result) # 递归调用回溯函数进行深度优先搜索 + result.append(airport) # 将当前机场添加到行程路径中 ``` ### GO From 93e0a18f0f58363435f02267ce417a13ca6314f5 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 28 May 2023 02:52:41 -0500 Subject: [PATCH 076/122] =?UTF-8?q?Update=200051.N=E7=9A=87=E5=90=8E.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0051.N皇后.md | 81 +++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/problems/0051.N皇后.md b/problems/0051.N皇后.md index bd1d1c9b..ab575bd8 100644 --- a/problems/0051.N皇后.md +++ b/problems/0051.N皇后.md @@ -348,48 +348,47 @@ class Solution { ```python class Solution: def solveNQueens(self, n: int) -> List[List[str]]: - if not n: return [] - board = [['.'] * n for _ in range(n)] - res = [] - def isVaild(board,row, col): - #判断同一列是否冲突 - for i in range(len(board)): - if board[i][col] == 'Q': - return False - # 判断左上角是否冲突 - i = row -1 - j = col -1 - while i>=0 and j>=0: - if board[i][j] == 'Q': - return False - i -= 1 - j -= 1 - # 判断右上角是否冲突 - i = row - 1 - j = col + 1 - while i>=0 and j < len(board): - if board[i][j] == 'Q': - return False - i -= 1 - j += 1 - return True + result = [] # 存储最终结果的二维字符串数组 + + chessboard = ['.' * n for _ in range(n)] # 初始化棋盘 + self.backtracking(n, 0, chessboard, result) # 回溯求解 + return [[''.join(row) for row in solution] for solution in result] # 返回结果集 + + def backtracking(self, n: int, row: int, chessboard: List[str], result: List[List[str]]) -> None: + if row == n: + result.append(chessboard[:]) # 棋盘填满,将当前解加入结果集 + return + + for col in range(n): + if self.isValid(row, col, chessboard): + chessboard[row] = chessboard[row][:col] + 'Q' + chessboard[row][col+1:] # 放置皇后 + self.backtracking(n, row + 1, chessboard, result) # 递归到下一行 + chessboard[row] = chessboard[row][:col] + '.' + chessboard[row][col+1:] # 回溯,撤销当前位置的皇后 + + def isValid(self, row: int, col: int, chessboard: List[str]) -> bool: + # 检查列 + for i in range(row): + if chessboard[i][col] == 'Q': + return False # 当前列已经存在皇后,不合法 + + # 检查 45 度角是否有皇后 + i, j = row - 1, col - 1 + while i >= 0 and j >= 0: + if chessboard[i][j] == 'Q': + return False # 左上方向已经存在皇后,不合法 + i -= 1 + j -= 1 + + # 检查 135 度角是否有皇后 + i, j = row - 1, col + 1 + while i >= 0 and j < len(chessboard): + if chessboard[i][j] == 'Q': + return False # 右上方向已经存在皇后,不合法 + i -= 1 + j += 1 + + return True # 当前位置合法 - def backtracking(board, row, n): - # 如果走到最后一行,说明已经找到一个解 - if row == n: - temp_res = [] - for temp in board: - temp_str = "".join(temp) - temp_res.append(temp_str) - res.append(temp_res) - for col in range(n): - if not isVaild(board, row, col): - continue - board[row][col] = 'Q' - backtracking(board, row+1, n) - board[row][col] = '.' - backtracking(board, 0, n) - return res ``` From 11581df17788327ea3114c1f98d332af2cd0d260 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 28 May 2023 17:58:11 -0500 Subject: [PATCH 077/122] =?UTF-8?q?Update=200455.=E5=88=86=E5=8F=91?= =?UTF-8?q?=E9=A5=BC=E5=B9=B2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0455.分发饼干.md | 43 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/problems/0455.分发饼干.md b/problems/0455.分发饼干.md index efadb433..f32af57b 100644 --- a/problems/0455.分发饼干.md +++ b/problems/0455.分发饼干.md @@ -177,32 +177,33 @@ class Solution { ``` ### Python - +贪心 大饼干优先 ```python class Solution: - # 思路1:优先考虑小胃口 - def findContentChildren(self, g: List[int], s: List[int]) -> int: - g.sort() - s.sort() - res = 0 - for i in range(len(s)): - if res = g[res]: #小饼干先喂饱小胃口 - res += 1 - return res + def findContentChildren(self, g, s): + g.sort() # 将孩子的贪心因子排序 + s.sort() # 将饼干的尺寸排序 + index = len(s) - 1 # 饼干数组的下标,从最后一个饼干开始 + result = 0 # 满足孩子的数量 + for i in range(len(g)-1, -1, -1): # 遍历胃口,从最后一个孩子开始 + if index >= 0 and s[index] >= g[i]: # 遍历饼干 + result += 1 + index -= 1 + return result + ``` - +贪心 小饼干优先 ```python class Solution: - # 思路2:优先考虑大胃口 - def findContentChildren(self, g: List[int], s: List[int]) -> int: - g.sort() - s.sort() - start, count = len(s) - 1, 0 - for index in range(len(g) - 1, -1, -1): # 先喂饱大胃口 - if start >= 0 and g[index] <= s[start]: - start -= 1 - count += 1 - return count + def findContentChildren(self, g, s): + g.sort() # 将孩子的贪心因子排序 + s.sort() # 将饼干的尺寸排序 + index = 0 + for i in range(len(s)): # 遍历饼干 + if index < len(g) and g[index] <= s[i]: # 如果当前孩子的贪心因子小于等于当前饼干尺寸 + index += 1 # 满足一个孩子,指向下一个孩子 + return index # 返回满足的孩子数目 + ``` ### Go From 18a406e14fd4f5a0d4b5c1a1e3f37410ff5e3fce Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 28 May 2023 18:45:13 -0500 Subject: [PATCH 078/122] =?UTF-8?q?Update=200376.=E6=91=86=E5=8A=A8?= =?UTF-8?q?=E5=BA=8F=E5=88=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0376.摆动序列.md | 87 ++++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 23 deletions(-) diff --git a/problems/0376.摆动序列.md b/problems/0376.摆动序列.md index ff388b55..285a0280 100644 --- a/problems/0376.摆动序列.md +++ b/problems/0376.摆动序列.md @@ -305,21 +305,43 @@ class Solution { ### Python -**贪心** +贪心(版本一) + +```python +class Solution: + def wiggleMaxLength(self, nums): + if len(nums) <= 1: + return len(nums) # 如果数组长度为0或1,则返回数组长度 + curDiff = 0 # 当前一对元素的差值 + preDiff = 0 # 前一对元素的差值 + result = 1 # 记录峰值的个数,初始为1(默认最右边的元素被视为峰值) + for i in range(len(nums) - 1): + curDiff = nums[i + 1] - nums[i] # 计算下一个元素与当前元素的差值 + # 如果遇到一个峰值 + if (preDiff <= 0 and curDiff > 0) or (preDiff >= 0 and curDiff < 0): + result += 1 # 峰值个数加1 + preDiff = curDiff # 注意这里,只在摆动变化的时候更新preDiff + return result # 返回最长摆动子序列的长度 + +``` +贪心(版本二) ```python class Solution: def wiggleMaxLength(self, nums: List[int]) -> int: - preC,curC,res = 0,0,1 #题目里nums长度大于等于1,当长度为1时,其实到不了for循环里去,所以不用考虑nums长度 + if len(nums) <= 1: + return len(nums) # 如果数组长度为0或1,则返回数组长度 + preDiff,curDiff ,result = 0,0,1 #题目里nums长度大于等于1,当长度为1时,其实到不了for循环里去,所以不用考虑nums长度 for i in range(len(nums) - 1): - curC = nums[i + 1] - nums[i] - if curC * preC <= 0 and curC !=0: #差值为0时,不算摆动 - res += 1 - preC = curC #如果当前差值和上一个差值为一正一负时,才需要用当前差值替代上一个差值 - return res + curDiff = nums[i + 1] - nums[i] + if curDiff * preDiff <= 0 and curDiff !=0: #差值为0时,不算摆动 + result += 1 + preDiff = curDiff #如果当前差值和上一个差值为一正一负时,才需要用当前差值替代上一个差值 + return result + ``` -**动态规划** +动态规划(版本一) ```python class Solution: @@ -341,25 +363,44 @@ class Solution: return max(dp[-1][0], dp[-1][1]) ``` +动态规划(版本二) + ```python class Solution: - def wiggleMaxLength(self, nums: List[int]) -> int: - # up i作为波峰最长的序列长度 - # down i作为波谷最长的序列长度 - n = len(nums) - # 长度为0和1的直接返回长度 - if n<2: return n - for i in range(1,n): - if nums[i]>nums[i-1]: - # nums[i] 为波峰,1. 前面是波峰,up值不变,2. 前面是波谷,down值加1 - # 目前up值取两者的较大值(其实down+1即可,可以推理前一步down和up最多相差1,所以down+1>=up) - up = max(up, down+1) - elif nums[i] nums[i]: + dp[i][1] = max(dp[i][1], dp[j][0] + 1) # 如果前一个数比当前数大,可以形成一个上升峰值,更新dp[i][1] + for j in range(i): + if nums[j] < nums[i]: + dp[i][0] = max(dp[i][0], dp[j][1] + 1) # 如果前一个数比当前数小,可以形成一个下降峰值,更新dp[i][0] + return max(dp[-1][0], dp[-1][1]) # 返回最大的摆动序列长度 + ``` +动态规划(版本三)优化 + +```python +class Solution: + def wiggleMaxLength(self, nums): + if len(nums) <= 1: + return len(nums) # 如果数组长度为0或1,则返回数组长度 + + up = down = 1 # 记录上升和下降摆动序列的最大长度 + for i in range(1, len(nums)): + if nums[i] > nums[i-1]: + up = down + 1 # 如果当前数比前一个数大,则可以形成一个上升峰值 + elif nums[i] < nums[i-1]: + down = up + 1 # 如果当前数比前一个数小,则可以形成一个下降峰值 + + return max(up, down) # 返回上升和下降摆动序列的最大长度 + + +``` ### Go **贪心** From d9887223965e84771503808e5685c1307998fabe Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Wed, 31 May 2023 23:44:40 -0500 Subject: [PATCH 079/122] =?UTF-8?q?Update=200053.=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E5=AD=90=E5=BA=8F=E5=92=8C.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0053.最大子序和.md | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/problems/0053.最大子序和.md b/problems/0053.最大子序和.md index 48f8be29..bc5e1d55 100644 --- a/problems/0053.最大子序和.md +++ b/problems/0053.最大子序和.md @@ -198,21 +198,35 @@ class Solution { ``` ### Python - +暴力法 ```python class Solution: - def maxSubArray(self, nums: List[int]) -> int: - result = -float('inf') + def maxSubArray(self, nums): + result = float('-inf') # 初始化结果为负无穷大 + count = 0 + for i in range(len(nums)): # 设置起始位置 + count = 0 + for j in range(i, len(nums)): # 从起始位置i开始遍历寻找最大值 + count += nums[j] + result = max(count, result) # 更新最大值 + return result + +``` +```python +class Solution: + def maxSubArray(self, nums): + result = float('-inf') # 初始化结果为负无穷大 count = 0 for i in range(len(nums)): count += nums[i] - if count > result: + if count > result: # 取区间累计的最大值(相当于不断确定最大子序终止位置) result = count - if count <= 0: + if count <= 0: # 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和 count = 0 return result -``` + +``` ### Go ```go From d65f7a32da277ab0020c3104c6cc3f51b0e51998 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 00:41:29 -0500 Subject: [PATCH 080/122] =?UTF-8?q?Update=200045.=E8=B7=B3=E8=B7=83?= =?UTF-8?q?=E6=B8=B8=E6=88=8FII.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0045.跳跃游戏II.md | 115 ++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/problems/0045.跳跃游戏II.md b/problems/0045.跳跃游戏II.md index 8a939582..2f0349b2 100644 --- a/problems/0045.跳跃游戏II.md +++ b/problems/0045.跳跃游戏II.md @@ -205,66 +205,81 @@ class Solution { ``` ### Python - +贪心(版本一) ```python class Solution: - def jump(self, nums: List[int]) -> int: - if len(nums) == 1: return 0 - ans = 0 - curDistance = 0 - nextDistance = 0 - for i in range(len(nums)): - nextDistance = max(i + nums[i], nextDistance) - if i == curDistance: - if curDistance != len(nums) - 1: - ans += 1 - curDistance = nextDistance - if nextDistance >= len(nums) - 1: break - return ans -``` - -```python -# 贪心版本二 -class Solution: - def jump(self, nums: List[int]) -> int: + def jump(self, nums): if len(nums) == 1: return 0 - curDistance, nextDistance = 0, 0 - step = 0 - for i in range(len(nums)-1): - nextDistance = max(nextDistance, nums[i]+i) - if i == curDistance: - curDistance = nextDistance - step += 1 - return step -``` -```python -# 贪心版本三 - 类似‘55-跳跃游戏’写法 -class Solution: - def jump(self, nums) -> int: - if len(nums)==1: return 0 - i = 0 - count = 0 - cover = 0 - while i<=cover: - for i in range(i,cover+1): - cover = max(nums[i]+i,cover) - if cover>=len(nums)-1: return count+1 - count+=1 + cur_distance = 0 # 当前覆盖最远距离下标 + ans = 0 # 记录走的最大步数 + next_distance = 0 # 下一步覆盖最远距离下标 + + for i in range(len(nums)): + next_distance = max(nums[i] + i, next_distance) # 更新下一步覆盖最远距离下标 + if i == cur_distance: # 遇到当前覆盖最远距离下标 + ans += 1 # 需要走下一步 + cur_distance = next_distance # 更新当前覆盖最远距离下标(相当于加油了) + if next_distance >= len(nums) - 1: # 当前覆盖最远距离达到数组末尾,不用再做ans++操作,直接结束 + break + + return ans + ``` +贪心(版本二) ```python -# 动态规划做法 +class Solution: + def jump(self, nums): + cur_distance = 0 # 当前覆盖的最远距离下标 + ans = 0 # 记录走的最大步数 + next_distance = 0 # 下一步覆盖的最远距离下标 + + for i in range(len(nums) - 1): # 注意这里是小于len(nums) - 1,这是关键所在 + next_distance = max(nums[i] + i, next_distance) # 更新下一步覆盖的最远距离下标 + if i == cur_distance: # 遇到当前覆盖的最远距离下标 + cur_distance = next_distance # 更新当前覆盖的最远距离下标 + ans += 1 + + return ans + +``` +贪心(版本三) 类似‘55-跳跃游戏’写法 + +```python +class Solution: + def jump(self, nums) -> int: + if len(nums)==1: # 如果数组只有一个元素,不需要跳跃,步数为0 + return 0 + + i = 0 # 当前位置 + count = 0 # 步数计数器 + cover = 0 # 当前能够覆盖的最远距离 + + while i <= cover: # 当前位置小于等于当前能够覆盖的最远距离时循环 + for i in range(i, cover+1): # 遍历从当前位置到当前能够覆盖的最远距离之间的所有位置 + cover = max(nums[i]+i, cover) # 更新当前能够覆盖的最远距离 + if cover >= len(nums)-1: # 如果当前能够覆盖的最远距离达到或超过数组的最后一个位置,直接返回步数+1 + return count+1 + count += 1 # 每一轮遍历结束后,步数+1 + + +``` +动态规划 +```python class Solution: def jump(self, nums: List[int]) -> int: - result = [10**4+1]*len(nums) - result[0]=0 - for i in range(len(nums)): - for j in range(nums[i]+1): - if i+j Date: Thu, 1 Jun 2023 00:45:16 -0500 Subject: [PATCH 081/122] =?UTF-8?q?Update=201005.K=E6=AC=A1=E5=8F=96?= =?UTF-8?q?=E5=8F=8D=E5=90=8E=E6=9C=80=E5=A4=A7=E5=8C=96=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E7=BB=84=E5=92=8C.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/1005.K次取反后最大化的数组和.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/problems/1005.K次取反后最大化的数组和.md b/problems/1005.K次取反后最大化的数组和.md index 27a575c7..343bef04 100644 --- a/problems/1005.K次取反后最大化的数组和.md +++ b/problems/1005.K次取反后最大化的数组和.md @@ -155,17 +155,23 @@ class Solution { ``` ### Python +贪心 ```python class Solution: def largestSumAfterKNegations(self, A: List[int], K: int) -> int: - A = sorted(A, key=abs, reverse=True) # 将A按绝对值从大到小排列 - for i in range(len(A)): - if K > 0 and A[i] < 0: + A.sort(key=lambda x: abs(x), reverse=True) # 第一步:按照绝对值降序排序数组A + + for i in range(len(A)): # 第二步:执行K次取反操作 + if A[i] < 0 and K > 0: A[i] *= -1 K -= 1 - if K > 0: - A[-1] *= (-1)**K #取A最后一个数只需要写-1 - return sum(A) + + if K % 2 == 1: # 第三步:如果K还有剩余次数,将绝对值最小的元素取反 + A[-1] *= -1 + + result = sum(A) # 第四步:计算数组A的元素和 + return result + ``` ### Go From 648b0d2edc8cbf6c46b673f54ef610ba0524d5e3 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 01:03:49 -0500 Subject: [PATCH 082/122] =?UTF-8?q?Update=200134.=E5=8A=A0=E6=B2=B9?= =?UTF-8?q?=E7=AB=99.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0134.加油站.md | 90 +++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/problems/0134.加油站.md b/problems/0134.加油站.md index f432bf0b..ad9acfbc 100644 --- a/problems/0134.加油站.md +++ b/problems/0134.加油站.md @@ -249,44 +249,74 @@ class Solution { ``` ### Python +暴力法 ```python -# 解法1 -class Solution: - def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: - n = len(gas) - cur_sum = 0 - min_sum = float('inf') - - for i in range(n): - cur_sum += gas[i] - cost[i] - min_sum = min(min_sum, cur_sum) - - if cur_sum < 0: return -1 - if min_sum >= 0: return 0 - - for j in range(n - 1, 0, -1): - min_sum += gas[j] - cost[j] - if min_sum >= 0: - return j - - return -1 -``` -```python -# 解法2 class Solution: def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: - start = 0 - curSum = 0 - totalSum = 0 + for i in range(len(cost)): + rest = gas[i] - cost[i] # 记录剩余油量 + index = (i + 1) % len(cost) # 下一个加油站的索引 + + while rest > 0 and index != i: # 模拟以i为起点行驶一圈(如果有rest==0,那么答案就不唯一了) + rest += gas[index] - cost[index] # 更新剩余油量 + index = (index + 1) % len(cost) # 更新下一个加油站的索引 + + if rest >= 0 and index == i: # 如果以i为起点跑一圈,剩余油量>=0,并且回到起始位置 + return i # 返回起始位置i + + return -1 # 所有起始位置都无法环绕一圈,返回-1 + +``` +贪心(版本一) +```python +class Solution: + def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: + curSum = 0 # 当前累计的剩余油量 + minFuel = float('inf') # 从起点出发,油箱里的油量最小值 + + for i in range(len(gas)): + rest = gas[i] - cost[i] + curSum += rest + if curSum < minFuel: + minFuel = curSum + + if curSum < 0: + return -1 # 情况1:整个行程的总消耗大于总供给,无法完成一圈 + + if minFuel >= 0: + return 0 # 情况2:从起点出发到任何一个加油站时油箱的剩余油量都不会小于0,可以从起点出发完成一圈 + + for i in range(len(gas) - 1, -1, -1): + rest = gas[i] - cost[i] + minFuel += rest + if minFuel >= 0: + return i # 情况3:找到一个位置使得从该位置出发油箱的剩余油量不会小于0,返回该位置的索引 + + return -1 # 无法完成一圈 + +``` +贪心(版本二) +```python +class Solution: + def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: + curSum = 0 # 当前累计的剩余油量 + totalSum = 0 # 总剩余油量 + start = 0 # 起始位置 + for i in range(len(gas)): curSum += gas[i] - cost[i] totalSum += gas[i] - cost[i] - if curSum < 0: - curSum = 0 - start = i + 1 - if totalSum < 0: return -1 + + if curSum < 0: # 当前累计剩余油量curSum小于0 + start = i + 1 # 起始位置更新为i+1 + curSum = 0 # curSum重新从0开始累计 + + if totalSum < 0: + return -1 # 总剩余油量totalSum小于0,说明无法环绕一圈 return start + + ``` ### Go From cfb10d2e2a24324a319fec0986dee0ef465394fc Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 01:35:04 -0500 Subject: [PATCH 083/122] =?UTF-8?q?Update=200135.=E5=88=86=E5=8F=91?= =?UTF-8?q?=E7=B3=96=E6=9E=9C.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0135.分发糖果.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/problems/0135.分发糖果.md b/problems/0135.分发糖果.md index 1ba1563f..cf3ccc8e 100644 --- a/problems/0135.分发糖果.md +++ b/problems/0135.分发糖果.md @@ -178,13 +178,21 @@ class Solution { class Solution: def candy(self, ratings: List[int]) -> int: candyVec = [1] * len(ratings) + + # 从前向后遍历,处理右侧比左侧评分高的情况 for i in range(1, len(ratings)): if ratings[i] > ratings[i - 1]: candyVec[i] = candyVec[i - 1] + 1 - for j in range(len(ratings) - 2, -1, -1): - if ratings[j] > ratings[j + 1]: - candyVec[j] = max(candyVec[j], candyVec[j + 1] + 1) - return sum(candyVec) + + # 从后向前遍历,处理左侧比右侧评分高的情况 + for i in range(len(ratings) - 2, -1, -1): + if ratings[i] > ratings[i + 1]: + candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1) + + # 统计结果 + result = sum(candyVec) + return result + ``` ### Go From ba3c7ec19b6b2ad3f95f35ae55f25b2459d710b3 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 01:37:41 -0500 Subject: [PATCH 084/122] =?UTF-8?q?Update=200860.=E6=9F=A0=E6=AA=AC?= =?UTF-8?q?=E6=B0=B4=E6=89=BE=E9=9B=B6.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0860.柠檬水找零.md | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/problems/0860.柠檬水找零.md b/problems/0860.柠檬水找零.md index d96e0879..5046df12 100644 --- a/problems/0860.柠檬水找零.md +++ b/problems/0860.柠檬水找零.md @@ -164,24 +164,39 @@ class Solution { ```python class Solution: def lemonadeChange(self, bills: List[int]) -> bool: - five, ten = 0, 0 + five = 0 + ten = 0 + twenty = 0 + for bill in bills: + # 情况一:收到5美元 if bill == 5: five += 1 - elif bill == 10: - if five < 1: return False - five -= 1 + + # 情况二:收到10美元 + if bill == 10: + if five <= 0: + return False ten += 1 - else: - if ten > 0 and five > 0: - ten -= 1 + five -= 1 + + # 情况三:收到20美元 + if bill == 20: + # 先尝试使用10美元和5美元找零 + if five > 0 and ten > 0: five -= 1 - elif five > 2: + ten -= 1 + #twenty += 1 + # 如果无法使用10美元找零,则尝试使用三张5美元找零 + elif five >= 3: five -= 3 + #twenty += 1 else: return False + return True + ``` ### Go From cd4d54616200a53d9a22fbd919edf905ad1d4dd1 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 02:16:29 -0500 Subject: [PATCH 085/122] =?UTF-8?q?Update=200435.=E6=97=A0=E9=87=8D?= =?UTF-8?q?=E5=8F=A0=E5=8C=BA=E9=97=B4.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0435.无重叠区间.md | 41 +++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/problems/0435.无重叠区间.md b/problems/0435.无重叠区间.md index 37ef819d..61f42d42 100644 --- a/problems/0435.无重叠区间.md +++ b/problems/0435.无重叠区间.md @@ -248,20 +248,45 @@ class Solution { ``` ### Python +贪心 基于左边界 ```python class Solution: def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: - if len(intervals) == 0: return 0 - intervals.sort(key=lambda x: x[1]) - count = 1 # 记录非交叉区间的个数 - end = intervals[0][1] # 记录区间分割点 + if not intervals: + return 0 + + intervals.sort(key=lambda x: x[0]) # 按照左边界升序排序 + count = 0 # 记录重叠区间数量 + for i in range(1, len(intervals)): - if end <= intervals[i][0]: + if intervals[i][0] < intervals[i - 1][1]: # 存在重叠区间 + intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]) # 更新重叠区间的右边界 count += 1 - end = intervals[i][1] - return len(intervals) - count -``` + + return count +``` +贪心 基于左边界 把452.用最少数量的箭引爆气球代码稍做修改 +```python +class Solution: + def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: + if not intervals: + return 0 + + intervals.sort(key=lambda x: x[0]) # 按照左边界升序排序 + + result = 1 # 不重叠区间数量,初始化为1,因为至少有一个不重叠的区间 + + for i in range(1, len(intervals)): + if intervals[i][0] >= intervals[i - 1][1]: # 没有重叠 + result += 1 + else: # 重叠情况 + intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]) # 更新重叠区间的右边界 + + return len(intervals) - result + + +``` ### Go ```go func eraseOverlapIntervals(intervals [][]int) int { From 458f94b8b94b6e9252dc23dc11f6627a4272e2b8 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 02:26:34 -0500 Subject: [PATCH 086/122] =?UTF-8?q?Update=200763.=E5=88=92=E5=88=86?= =?UTF-8?q?=E5=AD=97=E6=AF=8D=E5=8C=BA=E9=97=B4.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0763.划分字母区间.md | 111 +++++++++++++--------------------- 1 file changed, 42 insertions(+), 69 deletions(-) diff --git a/problems/0763.划分字母区间.md b/problems/0763.划分字母区间.md index fbcafdc8..158f76d6 100644 --- a/problems/0763.划分字母区间.md +++ b/problems/0763.划分字母区间.md @@ -231,83 +231,56 @@ class Solution{ ``` ### Python +贪心(版本一) ```python class Solution: def partitionLabels(self, s: str) -> List[int]: - hash = [0] * 26 - for i in range(len(s)): - hash[ord(s[i]) - ord('a')] = i + last_occurrence = {} # 存储每个字符最后出现的位置 + for i, ch in enumerate(s): + last_occurrence[ch] = i + result = [] - left = 0 - right = 0 - for i in range(len(s)): - right = max(right, hash[ord(s[i]) - ord('a')]) - if i == right: - result.append(right - left + 1) - left = i + 1 + start = 0 + end = 0 + for i, ch in enumerate(s): + end = max(end, last_occurrence[ch]) # 找到当前字符出现的最远位置 + if i == end: # 如果当前位置是最远位置,表示可以分割出一个区间 + result.append(end - start + 1) + start = i + 1 + return result - -# 解法二(不相交区间法) + +``` +贪心(版本二)与452.用最少数量的箭引爆气球 (opens new window)、435.无重叠区间 (opens new window)相同的思路。 +```python class Solution: - def partitionLabels(self, s: str) -> List[int]: - # 记录每个字母出现的区间 - def getBord(s): - hash = [[-float('inf')] * 2 for _ in range(26)] - for i in range(len(s)): - if hash[ord(s[i]) - ord('a')][0] == -float('inf'): - hash[ord(s[i]) - ord('a')][0] = i - hash[ord(s[i]) - ord('a')][1] = i - # 去除字符串中未出现的字母所占用区间 - hash_filter = [] - for item in hash: - if item[0] != -float('inf'): hash_filter.append(item) - return hash_filter - - # 得到无重叠区间题意中的输入样例格式:区间列表 - hash = getBord(s) - # 按照左边界从小到大排序 - hash.sort(key= lambda x: x[0]) - res = [] - left = 0 - # 记录最大右边界 - right = hash[0][1] + def countLabels(self, s): + # 初始化一个长度为26的区间列表,初始值为负无穷 + hash = [[float('-inf'), float('-inf')] for _ in range(26)] + hash_filter = [] + for i in range(len(s)): + if hash[ord(s[i]) - ord('a')][0] == float('-inf'): + hash[ord(s[i]) - ord('a')][0] = i + hash[ord(s[i]) - ord('a')][1] = i for i in range(len(hash)): - # 一旦下一区间左边界大于当前右边界,即可认为出现分割点 - if hash[i][0] > right: - res.append(right - left + 1) - left = hash[i][0] - # 实时更新最大右边界 - right = max(right, hash[i][1]) - # 最右侧区间(字符串长度为1时的特殊情况也包含于其中) - res.append(right - left + 1) + if hash[i][0] != float('-inf'): + hash_filter.append(hash[i]) + return hash_filter + + def partitionLabels(self, s): + res = [] + hash = self.countLabels(s) + hash.sort(key=lambda x: x[0]) # 按左边界从小到大排序 + rightBoard = hash[0][1] # 记录最大右边界 + leftBoard = 0 + for i in range(1, len(hash)): + if hash[i][0] > rightBoard: # 出现分割点 + res.append(rightBoard - leftBoard + 1) + leftBoard = hash[i][0] + rightBoard = max(rightBoard, hash[i][1]) + res.append(rightBoard - leftBoard + 1) # 最右端 return res - -# 解法三:区间合并法 (结合下一题 56. Merge Intervals 的写法) -class Solution: # - def partitionLabels(self, s: str) -> List[int]: - aaa = list(set(s)) - #aaa.sort() - bbb = list(s) - ccc = [] - for i in reversed(bbb): - ccc.append(i) - intervals = [] - for i in range(len(aaa)): - intervals.append([bbb.index(aaa[i]),len(bbb)-ccc.index(aaa[i])-1]) - # 先求出各个字母的存在区间,之后利用区间合并方法得出所有不相邻的最大区间。 - intervals.sort(key = lambda x:x[0]) - newinterval = [] - left, right = intervals[0][0], intervals[0][1] - for i in range(1,len(intervals)): - if intervals[i][0] in range(left, right+1): - right = max(intervals[i][1],intervals[i-1][1],right) - left = min(intervals[i-1][0],left) - else: - newinterval.append(right-left+1) - left = intervals[i][0] - right = intervals[i][1] - newinterval.append(right-left+1) - return newinterval + ``` ### Go From 352b51d550a6edcc9b6355b8773b91d4f69cb507 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 02:36:11 -0500 Subject: [PATCH 087/122] =?UTF-8?q?Update=200056.=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E5=8C=BA=E9=97=B4.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0056.合并区间.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/problems/0056.合并区间.md b/problems/0056.合并区间.md index 952d32af..ead2fb75 100644 --- a/problems/0056.合并区间.md +++ b/problems/0056.合并区间.md @@ -141,18 +141,24 @@ class Solution { ### Python ```python class Solution: - def merge(self, intervals: List[List[int]]) -> List[List[int]]: - if len(intervals) == 0: return intervals - intervals.sort(key=lambda x: x[0]) + def merge(self, intervals): result = [] - result.append(intervals[0]) + if len(intervals) == 0: + return result # 区间集合为空直接返回 + + intervals.sort(key=lambda x: x[0]) # 按照区间的左边界进行排序 + + result.append(intervals[0]) # 第一个区间可以直接放入结果集中 + for i in range(1, len(intervals)): - last = result[-1] - if last[1] >= intervals[i][0]: - result[-1] = [last[0], max(last[1], intervals[i][1])] + if result[-1][1] >= intervals[i][0]: # 发现重叠区间 + # 合并区间,只需要更新结果集最后一个区间的右边界,因为根据排序,左边界已经是最小的 + result[-1][1] = max(result[-1][1], intervals[i][1]) else: - result.append(intervals[i]) + result.append(intervals[i]) # 区间不重叠 + return result + ``` ### Go From 41dd31d826218bef8b25890b9dc31744fc3b5dd7 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 02:54:22 -0500 Subject: [PATCH 088/122] =?UTF-8?q?Update=200738.=E5=8D=95=E8=B0=83?= =?UTF-8?q?=E9=80=92=E5=A2=9E=E7=9A=84=E6=95=B0=E5=AD=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0738.单调递增的数字.md | 109 +++++++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 8 deletions(-) diff --git a/problems/0738.单调递增的数字.md b/problems/0738.单调递增的数字.md index 1cf8a0a6..d88ebbb0 100644 --- a/problems/0738.单调递增的数字.md +++ b/problems/0738.单调递增的数字.md @@ -164,17 +164,110 @@ class Solution { ### Python: +暴力 ```python class Solution: - def monotoneIncreasingDigits(self, n: int) -> int: - a = list(str(n)) - for i in range(len(a)-1,0,-1): - if int(a[i]) < int(a[i-1]): - a[i-1] = str(int(a[i-1]) - 1) - a[i:] = '9' * (len(a) - i) #python不需要设置flag值,直接按长度给9就好了 - return int("".join(a)) -``` + def checkNum(self, num): + max_digit = 10 + while num: + digit = num % 10 + if max_digit >= digit: + max_digit = digit + else: + return False + num //= 10 + return True + def monotoneIncreasingDigits(self, N): + for i in range(N, 0, -1): + if self.checkNum(i): + return i + return 0 + +``` +贪心(版本一) +```python +class Solution: + def monotoneIncreasingDigits(self, N: int) -> int: + # 将整数转换为字符串 + strNum = str(N) + # flag用来标记赋值9从哪里开始 + # 设置为字符串长度,为了防止第二个for循环在flag没有被赋值的情况下执行 + flag = len(strNum) + + # 从右往左遍历字符串 + for i in range(len(strNum) - 1, 0, -1): + # 如果当前字符比前一个字符小,说明需要修改前一个字符 + if strNum[i - 1] > strNum[i]: + flag = i # 更新flag的值,记录需要修改的位置 + # 将前一个字符减1,以保证递增性质 + strNum = strNum[:i - 1] + str(int(strNum[i - 1]) - 1) + strNum[i:] + + # 将flag位置及之后的字符都修改为9,以保证最大的递增数字 + for i in range(flag, len(strNum)): + strNum = strNum[:i] + '9' + strNum[i + 1:] + + # 将最终的字符串转换回整数并返回 + return int(strNum) + +``` +贪心(版本二) +```python +class Solution: + def monotoneIncreasingDigits(self, N: int) -> int: + # 将整数转换为字符串 + strNum = list(str(N)) + + # 从右往左遍历字符串 + for i in range(len(strNum) - 1, 0, -1): + # 如果当前字符比前一个字符小,说明需要修改前一个字符 + if strNum[i - 1] > strNum[i]: + strNum[i - 1] = str(int(strNum[i - 1]) - 1) # 将前一个字符减1 + # 将修改位置后面的字符都设置为9,因为修改前一个字符可能破坏了递增性质 + for j in range(i, len(strNum)): + strNum[j] = '9' + + # 将列表转换为字符串,并将字符串转换为整数并返回 + return int(''.join(strNum)) + + +``` +贪心(版本三) + +```python +class Solution: + def monotoneIncreasingDigits(self, N: int) -> int: + # 将整数转换为字符串 + strNum = list(str(N)) + + # 从右往左遍历字符串 + for i in range(len(strNum) - 1, 0, -1): + # 如果当前字符比前一个字符小,说明需要修改前一个字符 + if strNum[i - 1] > strNum[i]: + strNum[i - 1] = str(int(strNum[i - 1]) - 1) # 将前一个字符减1 + # 将修改位置后面的字符都设置为9,因为修改前一个字符可能破坏了递增性质 + strNum[i:] = '9' * (len(strNum) - i) + + # 将列表转换为字符串,并将字符串转换为整数并返回 + return int(''.join(strNum)) + +``` +贪心(版本四)精简 + +```python +class Solution: + def monotoneIncreasingDigits(self, N: int) -> int: + strNum = str(N) + for i in range(len(strNum) - 1, 0, -1): + # 如果当前字符比前一个字符小,说明需要修改前一个字符 + if strNum[i - 1] > strNum[i]: + # 将前一个字符减1,以保证递增性质 + # 使用字符串切片操作将修改后的前面部分与后面部分进行拼接 + strNum = strNum[:i - 1] + str(int(strNum[i - 1]) - 1) + '9' * (len(strNum) - i) + return int(strNum) + + +``` ### Go ```go func monotoneIncreasingDigits(N int) int { From ed4543b30042ab127a261b660054e54aaaf3102f Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 04:03:00 -0500 Subject: [PATCH 089/122] =?UTF-8?q?Update=200968.=E7=9B=91=E6=8E=A7?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0968.监控二叉树.md | 114 +++++++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 33 deletions(-) diff --git a/problems/0968.监控二叉树.md b/problems/0968.监控二叉树.md index fdec56ca..e2ba8ebf 100644 --- a/problems/0968.监控二叉树.md +++ b/problems/0968.监控二叉树.md @@ -371,56 +371,104 @@ class Solution { ### Python - +贪心(版本一) ```python class Solution: - def minCameraCover(self, root: TreeNode) -> int: - # Greedy Algo: + # Greedy Algo: # 从下往上安装摄像头:跳过leaves这样安装数量最少,局部最优 -> 全局最优 # 先给leaves的父节点安装,然后每隔两层节点安装一个摄像头,直到Head # 0: 该节点未覆盖 # 1: 该节点有摄像头 # 2: 该节点有覆盖 + def minCameraCover(self, root: TreeNode) -> int: + # 定义递归函数 + result = [0] # 用于记录摄像头的安装数量 + if self.traversal(root, result) == 0: + result[0] += 1 - result = 0 - # 从下往上遍历:后序(左右中) - def traversal(curr: TreeNode) -> int: - nonlocal result + return result[0] - if not curr: return 2 - left = traversal(curr.left) - right = traversal(curr.right) + + def traversal(self, cur: TreeNode, result: List[int]) -> int: + if not cur: + return 2 - # Case 1: - # 左右节点都有覆盖 - if left == 2 and right == 2: - return 0 + left = self.traversal(cur.left, result) + right = self.traversal(cur.right, result) - # Case 2: - # left == 0 && right == 0 左右节点无覆盖 - # left == 1 && right == 0 左节点有摄像头,右节点无覆盖 - # left == 0 && right == 1 左节点有无覆盖,右节点摄像头 - # left == 0 && right == 2 左节点无覆盖,右节点覆盖 - # left == 2 && right == 0 左节点覆盖,右节点无覆盖 - elif left == 0 or right == 0: - result += 1 - return 1 + # 情况1: 左右节点都有覆盖 + if left == 2 and right == 2: + return 0 - # Case 3: - # left == 1 && right == 2 左节点有摄像头,右节点有覆盖 - # left == 2 && right == 1 左节点有覆盖,右节点有摄像头 - # left == 1 && right == 1 左右节点都有摄像头 - elif left == 1 or right == 1: - return 2 + # 情况2: + # left == 0 && right == 0 左右节点无覆盖 + # left == 1 && right == 0 左节点有摄像头,右节点无覆盖 + # left == 0 && right == 1 左节点无覆盖,右节点有摄像头 + # left == 0 && right == 2 左节点无覆盖,右节点覆盖 + # left == 2 && right == 0 左节点覆盖,右节点无覆盖 + if left == 0 or right == 0: + result[0] += 1 + return 1 - # 其他情况前段代码均已覆盖 + # 情况3: + # left == 1 && right == 2 左节点有摄像头,右节点有覆盖 + # left == 2 && right == 1 左节点有覆盖,右节点有摄像头 + # left == 1 && right == 1 左右节点都有摄像头 + if left == 1 or right == 1: + return 2 - if traversal(root) == 0: - result += 1 - return result ``` +贪心(版本二)利用elif精简代码 +```python +class Solution: + # Greedy Algo: + # 从下往上安装摄像头:跳过leaves这样安装数量最少,局部最优 -> 全局最优 + # 先给leaves的父节点安装,然后每隔两层节点安装一个摄像头,直到Head + # 0: 该节点未覆盖 + # 1: 该节点有摄像头 + # 2: 该节点有覆盖 + def minCameraCover(self, root: TreeNode) -> int: + # 定义递归函数 + result = [0] # 用于记录摄像头的安装数量 + if self.traversal(root, result) == 0: + result[0] += 1 + return result[0] + + + def traversal(self, cur: TreeNode, result: List[int]) -> int: + if not cur: + return 2 + + left = self.traversal(cur.left, result) + right = self.traversal(cur.right, result) + + # 情况1: 左右节点都有覆盖 + if left == 2 and right == 2: + return 0 + + # 情况2: + # left == 0 && right == 0 左右节点无覆盖 + # left == 1 && right == 0 左节点有摄像头,右节点无覆盖 + # left == 0 && right == 1 左节点无覆盖,右节点有摄像头 + # left == 0 && right == 2 左节点无覆盖,右节点覆盖 + # left == 2 && right == 0 左节点覆盖,右节点无覆盖 + elif left == 0 or right == 0: + result[0] += 1 + return 1 + + # 情况3: + # left == 1 && right == 2 左节点有摄像头,右节点有覆盖 + # left == 2 && right == 1 左节点有覆盖,右节点有摄像头 + # left == 1 && right == 1 左右节点都有摄像头 + else: + return 2 + + + + +``` ### Go ```go From 28cf557d7c7bcf208764e083e4550c411f7c2ba0 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:02:00 -0500 Subject: [PATCH 090/122] =?UTF-8?q?Update=200509.=E6=96=90=E6=B3=A2?= =?UTF-8?q?=E9=82=A3=E5=A5=91=E6=95=B0.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0509.斐波那契数.md | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/problems/0509.斐波那契数.md b/problems/0509.斐波那契数.md index 08175058..195b68ac 100644 --- a/problems/0509.斐波那契数.md +++ b/problems/0509.斐波那契数.md @@ -203,18 +203,9 @@ class Solution { ``` ### Python +动态规划(版本一) ```python -class Solution: - def fib(self, n: int) -> int: - if n < 2: - return n - a, b, c = 0, 1, 0 - for i in range(1, n): - c = a + b - a, b = b, c - return c -# 动态规划 (注释版。无修饰) class Solution: def fib(self, n: int) -> int: @@ -238,7 +229,24 @@ class Solution: # 返回答案 return dp[n] -# 递归实现 +``` +动态规划(版本二) +```python +class Solution: + def fib(self, n: int) -> int: + if n < 2: + return n + a, b, c = 0, 1, 0 + for i in range(1, n): + c = a + b + a, b = b, c + return c + + +``` +递归(版本一) +```python + class Solution: def fib(self, n: int) -> int: if n < 2: From 69361bba979b9bc359a8ddfd43fc98014801c47f Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:14:15 -0500 Subject: [PATCH 091/122] =?UTF-8?q?Update=200070.=E7=88=AC=E6=A5=BC?= =?UTF-8?q?=E6=A2=AF.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0070.爬楼梯.md | 64 +++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/problems/0070.爬楼梯.md b/problems/0070.爬楼梯.md index a06ee91e..64c7e403 100644 --- a/problems/0070.爬楼梯.md +++ b/problems/0070.爬楼梯.md @@ -251,32 +251,66 @@ class Solution { ``` ### Python - +动态规划(版本一) ```python # 空间复杂度为O(n)版本 class Solution: def climbStairs(self, n: int) -> int: - # dp[i] 为第 i 阶楼梯有多少种方法爬到楼顶 - dp = [0]*(n+1) - dp[0] = 1 + if n <= 1: + return n + + dp = [0] * (n + 1) dp[1] = 1 - for i in range(2, n+1): - dp[i] = dp[i-1] + dp[i-2] + dp[2] = 2 + + for i in range(3, n + 1): + dp[i] = dp[i - 1] + dp[i - 2] + return dp[n] +``` +动态规划(版本二) +```python + +# 空间复杂度为O(3)版本 +class Solution: + def climbStairs(self, n: int) -> int: + if n <= 1: + return n + + dp = [0] * 3 + dp[1] = 1 + dp[2] = 2 + + for i in range(3, n + 1): + total = dp[1] + dp[2] + dp[1] = dp[2] + dp[2] = total + + return dp[2] + +``` +动态规划(版本三) +```python + # 空间复杂度为O(1)版本 class Solution: def climbStairs(self, n: int) -> int: - dp = [0]*(n+1) - dp[0] = 1 - dp[1] = 1 - for i in range(2,n+1): - tmp = dp[0] + dp[1] - dp[0] = dp[1] - dp[1] = tmp - return dp[1] -``` + if n <= 1: + return n + + prev1 = 1 + prev2 = 2 + + for i in range(3, n + 1): + total = prev1 + prev2 + prev1 = prev2 + prev2 = total + + return prev2 + +``` ### Go ```Go func climbStairs(n int) int { From 8e7d9f579ca973454fdd11f6857f1bd29664db3a Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:19:27 -0500 Subject: [PATCH 092/122] =?UTF-8?q?Update=200509.=E6=96=90=E6=B3=A2?= =?UTF-8?q?=E9=82=A3=E5=A5=91=E6=95=B0.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0509.斐波那契数.md | 38 ++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/problems/0509.斐波那契数.md b/problems/0509.斐波那契数.md index 195b68ac..cdbf860c 100644 --- a/problems/0509.斐波那契数.md +++ b/problems/0509.斐波那契数.md @@ -230,17 +230,41 @@ class Solution: return dp[n] ``` -动态规划(版本二) +态规划(版本二) +```python + +class Solution: + def fib(self, n: int) -> int: + if n <= 1: + return n + + dp = [0, 1] + + for i in range(2, n + 1): + total = dp[0] + dp[1] + dp[0] = dp[1] + dp[1] = total + + return dp[1] + + +``` +动态规划(版本三) ```python class Solution: def fib(self, n: int) -> int: - if n < 2: + if n <= 1: return n - a, b, c = 0, 1, 0 - for i in range(1, n): - c = a + b - a, b = b, c - return c + + prev1, prev2 = 0, 1 + + for _ in range(2, n + 1): + curr = prev1 + prev2 + prev1, prev2 = prev2, curr + + return prev2 + + ``` From b5494585fbff9e9ccb489674410c1f60401a9456 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:19:56 -0500 Subject: [PATCH 093/122] =?UTF-8?q?Update=200509.=E6=96=90=E6=B3=A2?= =?UTF-8?q?=E9=82=A3=E5=A5=91=E6=95=B0.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0509.斐波那契数.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0509.斐波那契数.md b/problems/0509.斐波那契数.md index cdbf860c..45a9cf6b 100644 --- a/problems/0509.斐波那契数.md +++ b/problems/0509.斐波那契数.md @@ -230,7 +230,7 @@ class Solution: return dp[n] ``` -态规划(版本二) +动态规划(版本二) ```python class Solution: From 283b74478afc69b4f8d8e6e33a98dfa30d659372 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 16:03:35 -0500 Subject: [PATCH 094/122] =?UTF-8?q?Update=200746.=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=9C=80=E5=B0=8F=E8=8A=B1=E8=B4=B9=E7=88=AC=E6=A5=BC=E6=A2=AF?= =?UTF-8?q?.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0746.使用最小花费爬楼梯.md | 46 +++++++++++++++++++---------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/problems/0746.使用最小花费爬楼梯.md b/problems/0746.使用最小花费爬楼梯.md index d7258d45..97d065ed 100644 --- a/problems/0746.使用最小花费爬楼梯.md +++ b/problems/0746.使用最小花费爬楼梯.md @@ -245,26 +245,42 @@ class Solution { ``` ### Python + +动态规划(版本一) ```python -# 第一步不支付费用 -class Solution: - def minCostClimbingStairs(self, cost: List[int]) -> int: - n = len(cost) - dp = [0]*(n+1) # 到达前两步费用为0 - for i in range(2, n+1): - dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]) - return dp[-1] -``` -```python -# 第一步支付费用 + class Solution: def minCostClimbingStairs(self, cost: List[int]) -> int: dp = [0] * (len(cost) + 1) - dp[0] = 0 - dp[1] = 0 + dp[0] = 0 # 初始值,表示从起点开始不需要花费体力 + dp[1] = 0 # 初始值,表示经过第一步不需要花费体力 + for i in range(2, len(cost) + 1): - dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i-2]) - return dp[len(cost)] + # 在第i步,可以选择从前一步(i-1)花费体力到达当前步,或者从前两步(i-2)花费体力到达当前步 + # 选择其中花费体力较小的路径,加上当前步的花费,更新dp数组 + dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]) + + return dp[len(cost)] # 返回到达楼顶的最小花费 + +``` +动态规划(版本二) + +```python +class Solution: + def minCostClimbingStairs(self, cost: List[int]) -> int: + dp0 = 0 # 初始值,表示从起点开始不需要花费体力 + dp1 = 0 # 初始值,表示经过第一步不需要花费体力 + + for i in range(2, len(cost) + 1): + # 在第i步,可以选择从前一步(i-1)花费体力到达当前步,或者从前两步(i-2)花费体力到达当前步 + # 选择其中花费体力较小的路径,加上当前步的花费,得到当前步的最小花费 + dpi = min(dp1 + cost[i - 1], dp0 + cost[i - 2]) + + dp0 = dp1 # 更新dp0为前一步的值,即上一次循环中的dp1 + dp1 = dpi # 更新dp1为当前步的最小花费 + + return dp1 # 返回到达楼顶的最小花费 + ``` ### Go From 6128c57e151b55d433ee74135838c303a9694908 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 17:13:23 -0500 Subject: [PATCH 095/122] =?UTF-8?q?Update=200062.=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E8=B7=AF=E5=BE=84.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0062.不同路径.md | 63 +++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/problems/0062.不同路径.md b/problems/0062.不同路径.md index bf436369..430dacdc 100644 --- a/problems/0062.不同路径.md +++ b/problems/0062.不同路径.md @@ -287,17 +287,70 @@ public: ``` ### Python - +递归 ```python -class Solution: # 动态规划 +class Solution: def uniquePaths(self, m: int, n: int) -> int: - dp = [[1 for i in range(n)] for j in range(m)] + if m == 1 or n == 1: + return 1 + return self.uniquePaths(m - 1, n) + self.uniquePaths(m, n - 1) + +``` +动态规划(版本一) +```python +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + # 创建一个二维列表用于存储唯一路径数 + dp = [[0] * n for _ in range(m)] + + # 设置第一行和第一列的基本情况 + for i in range(m): + dp[i][0] = 1 + for j in range(n): + dp[0][j] = 1 + + # 计算每个单元格的唯一路径数 for i in range(1, m): for j in range(1, n): - dp[i][j] = dp[i][j - 1] + dp[i - 1][j] + dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + + # 返回右下角单元格的唯一路径数 return dp[m - 1][n - 1] -``` +``` +动态规划(版本二) +```python +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + # 创建一个一维列表用于存储每列的唯一路径数 + dp = [1] * n + + # 计算每个单元格的唯一路径数 + for j in range(1, m): + for i in range(1, n): + dp[i] += dp[i - 1] + + # 返回右下角单元格的唯一路径数 + return dp[n - 1] +``` +数论 +```python +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + numerator = 1 # 分子 + denominator = m - 1 # 分母 + count = m - 1 # 计数器,表示剩余需要计算的乘积项个数 + t = m + n - 2 # 初始乘积项 + while count > 0: + numerator *= t # 计算乘积项的分子部分 + t -= 1 # 递减乘积项 + while denominator != 0 and numerator % denominator == 0: + numerator //= denominator # 约简分子 + denominator -= 1 # 递减分母 + count -= 1 # 计数器减1,继续下一项的计算 + return numerator # 返回最终的唯一路径数 + +``` ### Go ```Go From 906293efae7e1de646593c7ae560cab726b2662f Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 17:48:22 -0500 Subject: [PATCH 096/122] =?UTF-8?q?Update=200063.=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E8=B7=AF=E5=BE=84II.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0063.不同路径II.md | 153 +++++++++++++++++++++++++----------- 1 file changed, 107 insertions(+), 46 deletions(-) diff --git a/problems/0063.不同路径II.md b/problems/0063.不同路径II.md index 85130ab4..ac9cae75 100644 --- a/problems/0063.不同路径II.md +++ b/problems/0063.不同路径II.md @@ -271,69 +271,130 @@ class Solution { ### Python - +动态规划(版本一) ```python class Solution: - def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: - # 构造一个DP table - row = len(obstacleGrid) - col = len(obstacleGrid[0]) - dp = [[0 for _ in range(col)] for _ in range(row)] - dp[0][0] = 0 if obstacleGrid[0][0] == 1 else 1 - if dp[0][0] == 0: - return 0 # 如果第一个格子就是障碍,return 0 - # 第一行 - for i in range(1, col): - if obstacleGrid[0][i] == 1: - # 遇到障碍物时,直接退出循环,后面默认都是0 + def uniquePathsWithObstacles(self, obstacleGrid): + m = len(obstacleGrid) + n = len(obstacleGrid[0]) + if obstacleGrid[m - 1][n - 1] == 1 or obstacleGrid[0][0] == 1: + return 0 + dp = [[0] * n for _ in range(m)] + for i in range(m): + if obstacleGrid[i][0] == 0: # 遇到障碍物时,直接退出循环,后面默认都是0 + dp[i][0] = 1 + else: break - dp[0][i] = 1 - - # 第一列 - for i in range(1, row): - if obstacleGrid[i][0] == 1: - # 遇到障碍物时,直接退出循环,后面默认都是0 + for j in range(n): + if obstacleGrid[0][j] == 0: + dp[0][j] = 1 + else: break - dp[i][0] = 1 - # print(dp) + for i in range(1, m): + for j in range(1, n): + if obstacleGrid[i][j] == 1: + continue + dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + return dp[m - 1][n - 1] - for i in range(1, row): - for j in range(1, col): - if obstacleGrid[i][j] == 0: - dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - return dp[-1][-1] ``` +动态规划(版本二) +```python +class Solution: + def uniquePathsWithObstacles(self, obstacleGrid): + m = len(obstacleGrid) # 网格的行数 + n = len(obstacleGrid[0]) # 网格的列数 + + if obstacleGrid[m - 1][n - 1] == 1 or obstacleGrid[0][0] == 1: + # 如果起点或终点有障碍物,直接返回0 + return 0 + + dp = [[0] * n for _ in range(m)] # 创建一个二维列表用于存储路径数 + + # 设置起点的路径数为1 + dp[0][0] = 1 if obstacleGrid[0][0] == 0 else 0 + + # 计算第一列的路径数 + for i in range(1, m): + if obstacleGrid[i][0] == 0: + dp[i][0] = dp[i - 1][0] + + # 计算第一行的路径数 + for j in range(1, n): + if obstacleGrid[0][j] == 0: + dp[0][j] = dp[0][j - 1] + + # 计算其他位置的路径数 + for i in range(1, m): + for j in range(1, n): + if obstacleGrid[i][j] == 1: + continue + dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + + return dp[m - 1][n - 1] # 返回终点的路径数 + + +``` +动态规划(版本三) ```python class Solution: - """ - 使用一维dp数组 - """ + def uniquePathsWithObstacles(self, obstacleGrid): + if obstacleGrid[0][0] == 1: + return 0 + + dp = [0] * len(obstacleGrid[0]) # 创建一个一维列表用于存储路径数 + + # 初始化第一行的路径数 + for j in range(len(dp)): + if obstacleGrid[0][j] == 1: + dp[j] = 0 + elif j == 0: + dp[j] = 1 + else: + dp[j] = dp[j - 1] - def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: + # 计算其他行的路径数 + for i in range(1, len(obstacleGrid)): + for j in range(len(dp)): + if obstacleGrid[i][j] == 1: + dp[j] = 0 + elif j != 0: + dp[j] = dp[j] + dp[j - 1] + + return dp[-1] # 返回最后一个元素,即终点的路径数 + +``` +动态规划(版本四) + +```python +class Solution: + def uniquePathsWithObstacles(self, obstacleGrid): + if obstacleGrid[0][0] == 1: + return 0 + m, n = len(obstacleGrid), len(obstacleGrid[0]) - - # 初始化dp数组 - # 该数组缓存当前行 - curr = [0] * n + + dp = [0] * n # 创建一个一维列表用于存储路径数 + + # 初始化第一行的路径数 for j in range(n): if obstacleGrid[0][j] == 1: break - curr[j] = 1 + dp[j] = 1 - for i in range(1, m): # 从第二行开始 - for j in range(n): # 从第一列开始,因为第一列可能有障碍物 - # 有障碍物处无法通行,状态就设成0 + # 计算其他行的路径数 + for i in range(1, m): + if obstacleGrid[i][0] == 1: + dp[0] = 0 + for j in range(1, n): if obstacleGrid[i][j] == 1: - curr[j] = 0 - elif j > 0: - # 等价于 - # dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - curr[j] = curr[j] + curr[j - 1] - # 隐含的状态更新 - # dp[i][0] = dp[i - 1][0] + dp[j] = 0 + else: + dp[j] += dp[j - 1] + + return dp[-1] # 返回最后一个元素,即终点的路径数 - return curr[n - 1] ``` From 442008bb8ecfa3f64c9c9402e4c59c46575a17ae Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 22:44:10 -0500 Subject: [PATCH 097/122] =?UTF-8?q?Update=200343.=E6=95=B4=E6=95=B0?= =?UTF-8?q?=E6=8B=86=E5=88=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0343.整数拆分.md | 68 +++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/problems/0343.整数拆分.md b/problems/0343.整数拆分.md index c2fbbdde..f24e2985 100644 --- a/problems/0343.整数拆分.md +++ b/problems/0343.整数拆分.md @@ -245,20 +245,68 @@ class Solution { ``` ### Python +动态规划(版本一) ```python class Solution: - def integerBreak(self, n: int) -> int: - dp = [0] * (n + 1) - dp[2] = 1 + # 假设对正整数 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] + def integerBreak(self, n): + dp = [0] * (n + 1) # 创建一个大小为n+1的数组来存储计算结果 + dp[2] = 1 # 初始化dp[2]为1,因为当n=2时,只有一个切割方式1+1=2,乘积为1 + + # 从3开始计算,直到n for i in range(3, n + 1): - # 假设对正整数 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 / 2 + 1): - dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j])) - return dp[n] -``` + # 遍历所有可能的切割点 + for j in range(1, i // 2 + 1): + # 计算切割点j和剩余部分(i-j)的乘积,并与之前的结果进行比较取较大值 + + dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j)) + + return dp[n] # 返回最终的计算结果 + +``` +动态规划(版本二) +```python +class Solution: + def integerBreak(self, n): + if n <= 3: + return 1 * (n - 1) # 对于n小于等于3的情况,返回1 * (n - 1) + + dp = [0] * (n + 1) # 创建一个大小为n+1的数组来存储最大乘积结果 + dp[1] = 1 # 当n等于1时,最大乘积为1 + dp[2] = 2 # 当n等于2时,最大乘积为2 + dp[3] = 3 # 当n等于3时,最大乘积为3 + + # 从4开始计算,直到n + for i in range(4, n + 1): + # 遍历所有可能的切割点 + for j in range(1, i // 2 + 1): + # 计算切割点j和剩余部分(i - j)的乘积,并与之前的结果进行比较取较大值 + dp[i] = max(dp[i], dp[i - j] * dp[j]) + + return dp[n] # 返回整数拆分的最大乘积结果 + +``` +贪心 +```python +class Solution: + def integerBreak(self, n): + if n == 2: # 当n等于2时,只有一种拆分方式:1+1=2,乘积为1 + return 1 + if n == 3: # 当n等于3时,只有一种拆分方式:1+1+1=3,乘积为1 + return 2 + if n == 4: # 当n等于4时,有两种拆分方式:2+2=4和1+1+1+1=4,乘积都为4 + return 4 + result = 1 + while n > 4: + result *= 3 # 每次乘以3,因为3的乘积比其他数字更大 + n -= 3 # 每次减去3 + result *= n # 将剩余的n乘以最后的结果 + return result + +``` ### Go ```go func integerBreak(n int) int { From 41224f61bd37979847ee6258cda1282aa227befe Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Thu, 1 Jun 2023 22:50:49 -0500 Subject: [PATCH 098/122] =?UTF-8?q?Update=200096.=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E7=9A=84=E4=BA=8C=E5=8F=89=E6=90=9C=E7=B4=A2=E6=A0=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0096.不同的二叉搜索树.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/problems/0096.不同的二叉搜索树.md b/problems/0096.不同的二叉搜索树.md index 9f41906d..368a5747 100644 --- a/problems/0096.不同的二叉搜索树.md +++ b/problems/0096.不同的二叉搜索树.md @@ -197,12 +197,13 @@ class Solution { ```python class Solution: def numTrees(self, n: int) -> int: - dp = [0] * (n + 1) - dp[0], dp[1] = 1, 1 - for i in range(2, n + 1): - for j in range(1, i + 1): - dp[i] += dp[j - 1] * dp[i - j] - return dp[-1] + dp = [0] * (n + 1) # 创建一个长度为n+1的数组,初始化为0 + dp[0] = 1 # 当n为0时,只有一种情况,即空树,所以dp[0] = 1 + for i in range(1, n + 1): # 遍历从1到n的每个数字 + for j in range(1, i + 1): # 对于每个数字i,计算以i为根节点的二叉搜索树的数量 + dp[i] += dp[j - 1] * dp[i - j] # 利用动态规划的思想,累加左子树和右子树的组合数量 + return dp[n] # 返回以1到n为节点的二叉搜索树的总数量 + ``` ### Go From 09ab0e48738d17c184c197d5043a2e8423d0686d Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 3 Jun 2023 20:10:36 -0500 Subject: [PATCH 099/122] =?UTF-8?q?Update=20=E8=83=8C=E5=8C=85=E7=90=86?= =?UTF-8?q?=E8=AE=BA=E5=9F=BA=E7=A1=8001=E8=83=8C=E5=8C=85-1.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/背包理论基础01背包-1.md | 75 +++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/problems/背包理论基础01背包-1.md b/problems/背包理论基础01背包-1.md index c2525248..9df2fc4c 100644 --- a/problems/背包理论基础01背包-1.md +++ b/problems/背包理论基础01背包-1.md @@ -339,38 +339,63 @@ public class BagProblem { ``` ### python - +无参数版 ```python -def test_2_wei_bag_problem1(bag_size, weight, value) -> int: - rows, cols = len(weight), bag_size + 1 - dp = [[0 for _ in range(cols)] for _ in range(rows)] +def test_2_wei_bag_problem1(): + weight = [1, 3, 4] + value = [15, 20, 30] + bagweight = 4 - # 初始化dp数组. - for i in range(rows): - dp[i][0] = 0 - first_item_weight, first_item_value = weight[0], value[0] - for j in range(1, cols): - if first_item_weight <= j: - dp[0][j] = first_item_value + # 二维数组 + dp = [[0] * (bagweight + 1) for _ in range(len(weight))] - # 更新dp数组: 先遍历物品, 再遍历背包. - for i in range(1, len(weight)): - cur_weight, cur_val = weight[i], value[i] - for j in range(1, cols): - if cur_weight > j: # 说明背包装不下当前物品. - dp[i][j] = dp[i - 1][j] # 所以不装当前物品. - else: - # 定义dp数组: dp[i][j] 前i个物品里,放进容量为j的背包,价值总和最大是多少。 - dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cur_weight]+ cur_val) + # 初始化 + for j in range(weight[0], bagweight + 1): + dp[0][j] = value[0] - print(dp) + # weight数组的大小就是物品个数 + for i in range(1, len(weight)): # 遍历物品 + for j in range(bagweight + 1): # 遍历背包容量 + if j < weight[i]: + dp[i][j] = dp[i - 1][j] + else: + dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]) + print(dp[len(weight) - 1][bagweight]) + +test_2_wei_bag_problem1() + +``` +有参数版 +```python +def test_2_wei_bag_problem1(weight, value, bagweight): + # 二维数组 + dp = [[0] * (bagweight + 1) for _ in range(len(weight))] + + # 初始化 + for j in range(weight[0], bagweight + 1): + dp[0][j] = value[0] + + # weight数组的大小就是物品个数 + for i in range(1, len(weight)): # 遍历物品 + for j in range(bagweight + 1): # 遍历背包容量 + if j < weight[i]: + dp[i][j] = dp[i - 1][j] + else: + dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]) + + return dp[len(weight) - 1][bagweight] if __name__ == "__main__": - bag_size = 4 - weight = [1, 3, 4] - value = [15, 20, 30] - test_2_wei_bag_problem1(bag_size, weight, value) + + weight = [1, 3, 4] + value = [15, 20, 30] + bagweight = 4 + + result = test_2_wei_bag_problem1(weight, value, bagweight) + print(result) + + ``` From 37fdbd369e16ab7739bfdfd46b9fd8dbcdf514b9 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:59:37 -0500 Subject: [PATCH 100/122] =?UTF-8?q?Update=20=E8=83=8C=E5=8C=85=E7=90=86?= =?UTF-8?q?=E8=AE=BA=E5=9F=BA=E7=A1=8001=E8=83=8C=E5=8C=85-2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/背包理论基础01背包-2.md | 37 +++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/problems/背包理论基础01背包-2.md b/problems/背包理论基础01背包-2.md index 3b798334..d481e044 100644 --- a/problems/背包理论基础01背包-2.md +++ b/problems/背包理论基础01背包-2.md @@ -246,25 +246,46 @@ int main() { ### Python +无参版 ```python def test_1_wei_bag_problem(): weight = [1, 3, 4] value = [15, 20, 30] - bag_weight = 4 - # 初始化: 全为0 - dp = [0] * (bag_weight + 1) + bagWeight = 4 - # 先遍历物品, 再遍历背包容量 - for i in range(len(weight)): - for j in range(bag_weight, weight[i] - 1, -1): - # 递归公式 + # 初始化 + dp = [0] * (bagWeight + 1) + for i in range(len(weight)): # 遍历物品 + for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) - print(dp) + print(dp[bagWeight]) + test_1_wei_bag_problem() ``` +有参版 +```python +def test_1_wei_bag_problem(weight, value, bagWeight): + # 初始化 + dp = [0] * (bagWeight + 1) + for i in range(len(weight)): # 遍历物品 + for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量 + dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) + return dp[bagWeight] + + +if __name__ == "__main__": + + weight = [1, 3, 4] + value = [15, 20, 30] + bagweight = 4 + + result = test_1_wei_bag_problem(weight, value, bagweight) + print(result) + +``` ### Go ```go func test_1_wei_bag_problem(weight, value []int, bagWeight int) int { From 5ab0a94c7152431b23303a6e64dc18a07c9ed37f Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 05:36:45 -0500 Subject: [PATCH 101/122] =?UTF-8?q?Update=200416.=E5=88=86=E5=89=B2?= =?UTF-8?q?=E7=AD=89=E5=92=8C=E5=AD=90=E9=9B=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0416.分割等和子集.md | 107 +++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 42 deletions(-) diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md index 8115e18e..bb210e29 100644 --- a/problems/0416.分割等和子集.md +++ b/problems/0416.分割等和子集.md @@ -296,62 +296,85 @@ false true false false false true true false false false true true ``` ### Python: +卡哥版 ```python -# 一维度数组解法 class Solution: def canPartition(self, nums: List[int]) -> bool: - target = sum(nums) - if target % 2 == 1: return False - target //= 2 - dp = [0] * (target + 1) - for i in range(len(nums)): - for j in range(target, nums[i] - 1, -1): - dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]) - return target == dp[target] + _sum = 0 + + # dp[i]中的i表示背包内总和 + # 题目中说:每个数组中的元素不会超过 100,数组的大小不会超过 200 + # 总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了 + dp = [0] * 10001 + for num in nums: + _sum += num + # 也可以使用内置函数一步求和 + # _sum = sum(nums) + if _sum % 2 == 1: + return False + target = _sum // 2 + + # 开始 0-1背包 + for num in nums: + for j in range(target, num - 1, -1): # 每一个元素一定是不可重复放入,所以从大到小遍历 + dp[j] = max(dp[j], dp[j - num] + num) + + # 集合中的元素正好可以凑成总和target + if dp[target] == target: + return True + return False + ``` - +二维DP版 ```python -# 二维度数组解法 class Solution: def canPartition(self, nums: List[int]) -> bool: - target = sum(nums) - nums = sorted(nums) + + total_sum = sum(nums) - # 做最初的判断 - if target % 2 != 0: + if total_sum % 2 != 0: return False - # 找到 target value 可以认为这个是背包的体积 - target = target // 2 + target_sum = total_sum // 2 + dp = [[False] * (target_sum + 1) for _ in range(len(nums) + 1)] - row = len(nums) - col = target + 1 + # 初始化第一行(空子集可以得到和为0) + for i in range(len(nums) + 1): + dp[i][0] = True - # 定义 dp table - dp = [[0 for _ in range(col)] for _ in range(row)] - - # 初始 dp value - for i in range(row): - dp[i][0] = 0 - - for j in range(1, target): - if nums[0] <= j: - dp[0][j] = nums[0] - - # 遍历 先遍历物品再遍历背包 - for i in range(1, row): - - cur_weight = nums[i] - cur_value = nums[i] - - for j in range(1, col): - if cur_weight > j: + for i in range(1, len(nums) + 1): + for j in range(1, target_sum + 1): + if j < nums[i - 1]: + # 当前数字大于目标和时,无法使用该数字 dp[i][j] = dp[i - 1][j] else: - dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cur_weight] + cur_value) - - # 输出结果 - return dp[-1][col - 1] == target + # 当前数字小于等于目标和时,可以选择使用或不使用该数字 + dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i - 1]] + + return dp[len(nums)][target_sum] + +``` +一维DP版 +```python +class Solution: + def canPartition(self, nums: List[int]) -> bool: + + total_sum = sum(nums) + + if total_sum % 2 != 0: + return False + + target_sum = total_sum // 2 + dp = [False] * (target_sum + 1) + dp[0] = True + + for num in nums: + # 从target_sum逆序迭代到num,步长为-1 + for i in range(target_sum, num - 1, -1): + dp[i] = dp[i] or dp[i - num] + + return dp[target_sum] + ``` ### Go: From 212e8f8b393dac2730bf0136b09838cdbc4d24b8 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 05:48:05 -0500 Subject: [PATCH 102/122] =?UTF-8?q?Update=200416.=E5=88=86=E5=89=B2?= =?UTF-8?q?=E7=AD=89=E5=92=8C=E5=AD=90=E9=9B=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0416.分割等和子集.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md index bb210e29..7f6e3d5b 100644 --- a/problems/0416.分割等和子集.md +++ b/problems/0416.分割等和子集.md @@ -349,7 +349,10 @@ class Solution: dp[i][j] = dp[i - 1][j] else: # 当前数字小于等于目标和时,可以选择使用或不使用该数字 - dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i - 1]] + # dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i - 1]] + + # 当前数字小于等于目标和时,可以选择使用或不使用该数字的最大值 + dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i - 1]]) return dp[len(nums)][target_sum] @@ -371,9 +374,10 @@ class Solution: for num in nums: # 从target_sum逆序迭代到num,步长为-1 for i in range(target_sum, num - 1, -1): - dp[i] = dp[i] or dp[i - num] + dp[i] = max(dp[i], dp[i - num]) # 可以将其改成 dp[i] = dp[i] or dp[i - num] return dp[target_sum] + ``` From cb9da4fa38d861567a67ef0bd8467461e9ef90ff Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 05:48:34 -0500 Subject: [PATCH 103/122] =?UTF-8?q?Update=200416.=E5=88=86=E5=89=B2?= =?UTF-8?q?=E7=AD=89=E5=92=8C=E5=AD=90=E9=9B=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0416.分割等和子集.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md index 7f6e3d5b..341362ba 100644 --- a/problems/0416.分割等和子集.md +++ b/problems/0416.分割等和子集.md @@ -351,7 +351,7 @@ class Solution: # 当前数字小于等于目标和时,可以选择使用或不使用该数字 # dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i - 1]] - # 当前数字小于等于目标和时,可以选择使用或不使用该数字的最大值 + # 当前数字小于等于目标和时,选择使用或不使用该数字的最大值 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i - 1]]) return dp[len(nums)][target_sum] From 285b6ab370ea7e08d768574751e1971c3ff11810 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 05:53:50 -0500 Subject: [PATCH 104/122] =?UTF-8?q?Update=200416.=E5=88=86=E5=89=B2?= =?UTF-8?q?=E7=AD=89=E5=92=8C=E5=AD=90=E9=9B=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0416.分割等和子集.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md index 341362ba..1f129312 100644 --- a/problems/0416.分割等和子集.md +++ b/problems/0416.分割等和子集.md @@ -349,10 +349,7 @@ class Solution: dp[i][j] = dp[i - 1][j] else: # 当前数字小于等于目标和时,可以选择使用或不使用该数字 - # dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i - 1]] - - # 当前数字小于等于目标和时,选择使用或不使用该数字的最大值 - dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i - 1]]) + dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i - 1]] return dp[len(nums)][target_sum] @@ -374,8 +371,7 @@ class Solution: for num in nums: # 从target_sum逆序迭代到num,步长为-1 for i in range(target_sum, num - 1, -1): - dp[i] = max(dp[i], dp[i - num]) # 可以将其改成 dp[i] = dp[i] or dp[i - num] - + dp[i] = dp[i] or dp[i - num] return dp[target_sum] From 980c98ca0d15304b701d4cfbcc52a1d59ab736bf Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 06:27:03 -0500 Subject: [PATCH 105/122] =?UTF-8?q?Update=201049.=E6=9C=80=E5=90=8E?= =?UTF-8?q?=E4=B8=80=E5=9D=97=E7=9F=B3=E5=A4=B4=E7=9A=84=E9=87=8D=E9=87=8F?= =?UTF-8?q?II.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/1049.最后一块石头的重量II.md | 77 ++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 8 deletions(-) diff --git a/problems/1049.最后一块石头的重量II.md b/problems/1049.最后一块石头的重量II.md index a8150b1f..a978b802 100644 --- a/problems/1049.最后一块石头的重量II.md +++ b/problems/1049.最后一块石头的重量II.md @@ -224,18 +224,79 @@ class Solution { ``` ### Python: +卡哥版 ```python class Solution: def lastStoneWeightII(self, stones: List[int]) -> int: - sumweight = sum(stones) - target = sumweight // 2 - dp = [0] * (target + 1) - for i in range(len(stones)): - for j in range(target, stones[i] - 1, -1): - dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]) - return sumweight - 2 * dp[target] -``` + dp = [0] * 15001 + total_sum = sum(stones) + target = total_sum // 2 + for stone in stones: # 遍历物品 + for j in range(target, stone - 1, -1): # 遍历背包 + dp[j] = max(dp[j], dp[j - stone] + stone) + + return total_sum - dp[target] - dp[target] + +``` +二维DP版 +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + total_sum = sum(stones) + target = total_sum // 2 + + # 创建二维dp数组,行数为石头的数量加1,列数为target加1 + # dp[i][j]表示前i个石头能否组成总重量为j + dp = [[False] * (target + 1) for _ in range(len(stones) + 1)] + + # 初始化第一列,表示总重量为0时,前i个石头都能组成 + for i in range(len(stones) + 1): + dp[i][0] = True + + for i in range(1, len(stones) + 1): + for j in range(1, target + 1): + # 如果当前石头重量大于当前目标重量j,则无法选择该石头 + if stones[i - 1] > j: + dp[i][j] = dp[i - 1][j] + else: + # 可选择该石头或不选择该石头 + dp[i][j] = dp[i - 1][j] or dp[i - 1][j - stones[i - 1]] + + # 找到最大的重量i,使得dp[len(stones)][i]为True + # 返回总重量减去两倍的最接近总重量一半的重量 + for i in range(target, -1, -1): + if dp[len(stones)][i]: + return total_sum - 2 * i + + return 0 + + +``` +一维DP版 +```python +class Solution: + def lastStoneWeightII(self, stones): + total_sum = sum(stones) + target = total_sum // 2 + dp = [False] * (target + 1) + dp[0] = True + + for stone in stones: + for j in range(target, stone - 1, -1): + # 判断当前重量是否可以通过选择之前的石头得到或选择当前石头和之前的石头得到 + dp[j] = dp[j] or dp[j - stone] + + for i in range(target, -1, -1): + if dp[i]: + # 返回剩余石头的重量,即总重量减去两倍的最接近总重量一半的重量 + return total_sum - 2 * i + + return 0 + + + +``` ### Go: ```go func lastStoneWeightII(stones []int) int { From 8b69827fca7cfe9bd6e2fff92d7774189b489a32 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 07:01:19 -0500 Subject: [PATCH 106/122] =?UTF-8?q?Update=200494.=E7=9B=AE=E6=A0=87?= =?UTF-8?q?=E5=92=8C.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0494.目标和.md | 85 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 10 deletions(-) diff --git a/problems/0494.目标和.md b/problems/0494.目标和.md index c9f88892..32931e6b 100644 --- a/problems/0494.目标和.md +++ b/problems/0494.目标和.md @@ -293,19 +293,84 @@ class Solution { ``` ### Python +回溯版 +```python +class Solution: + + + def backtracking(self, candidates, target, total, startIndex, path, result): + if total == target: + result.append(path[:]) # 将当前路径的副本添加到结果中 + # 如果 sum + candidates[i] > target,则停止遍历 + for i in range(startIndex, len(candidates)): + if total + candidates[i] > target: + break + total += candidates[i] + path.append(candidates[i]) + self.backtracking(candidates, target, total, i + 1, path, result) + total -= candidates[i] + path.pop() + + def findTargetSumWays(self, nums: List[int], target: int) -> int: + total = sum(nums) + if target > total: + return 0 # 此时没有方案 + if (target + total) % 2 != 0: + return 0 # 此时没有方案,两个整数相加时要注意数值溢出的问题 + bagSize = (target + total) // 2 # 转化为组合总和问题,bagSize就是目标和 + + # 以下是回溯法代码 + result = [] + nums.sort() # 需要对nums进行排序 + self.backtracking(nums, bagSize, 0, 0, [], result) + return len(result) + +``` +二维DP ```python class Solution: def findTargetSumWays(self, nums: List[int], target: int) -> int: - sumValue = sum(nums) - #注意边界条件为 target>sumValue or target<-sumValue or (sumValue + target) % 2 == 1 - if abs(target) > sumValue or (sumValue + target) % 2 == 1: return 0 - bagSize = (sumValue + target) // 2 - dp = [0] * (bagSize + 1) - dp[0] = 1 - for i in range(len(nums)): - for j in range(bagSize, nums[i] - 1, -1): - dp[j] += dp[j - nums[i]] - return dp[bagSize] + total_sum = sum(nums) # 计算nums的总和 + if abs(target) > total_sum: + return 0 # 此时没有方案 + if (target + total_sum) % 2 == 1: + return 0 # 此时没有方案 + target_sum = (target + total_sum) // 2 # 目标和 + + # 创建二维动态规划数组,行表示选取的元素数量,列表示累加和 + dp = [[0] * (target_sum + 1) for _ in range(len(nums) + 1)] + + # 初始化状态 + dp[0][0] = 1 + + # 动态规划过程 + for i in range(1, len(nums) + 1): + for j in range(target_sum + 1): + dp[i][j] = dp[i - 1][j] # 不选取当前元素 + if j >= nums[i - 1]: + dp[i][j] += dp[i - 1][j - nums[i - 1]] # 选取当前元素 + + return dp[len(nums)][target_sum] # 返回达到目标和的方案数 + + +``` +一维DP +```python +class Solution: + def findTargetSumWays(self, nums: List[int], target: int) -> int: + total_sum = sum(nums) # 计算nums的总和 + if abs(target) > total_sum: + return 0 # 此时没有方案 + if (target + total_sum) % 2 == 1: + return 0 # 此时没有方案 + target_sum = (target + total_sum) // 2 # 目标和 + dp = [0] * (target_sum + 1) # 创建动态规划数组,初始化为0 + dp[0] = 1 # 当目标和为0时,只有一种方案,即什么都不选 + for num in nums: + for j in range(target_sum, num - 1, -1): + dp[j] += dp[j - num] # 状态转移方程,累加不同选择方式的数量 + return dp[target_sum] # 返回达到目标和的方案数 + ``` ### Go From b9faa11851fa43443380efefade020a02e0744f4 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 07:41:05 -0500 Subject: [PATCH 107/122] =?UTF-8?q?Update=200474.=E4=B8=80=E5=92=8C?= =?UTF-8?q?=E9=9B=B6.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0474.一和零.md | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/problems/0474.一和零.md b/problems/0474.一和零.md index 6a178a25..145f64aa 100644 --- a/problems/0474.一和零.md +++ b/problems/0474.一和零.md @@ -210,19 +210,35 @@ class Solution { ``` ### Python +DP(版本一) ```python class Solution: def findMaxForm(self, strs: List[str], m: int, n: int) -> int: - dp = [[0] * (n + 1) for _ in range(m + 1)] # 默认初始化0 + dp = [[0] * (n + 1) for _ in range(m + 1)] # 创建二维动态规划数组,初始化为0 + for s in strs: # 遍历物品 + zeroNum = s.count('0') # 统计0的个数 + oneNum = len(s) - zeroNum # 统计1的个数 + for i in range(m, zeroNum - 1, -1): # 遍历背包容量且从后向前遍历 + for j in range(n, oneNum - 1, -1): + dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1) # 状态转移方程 + return dp[m][n] + +``` +DP(版本二) +```python +class Solution: + def findMaxForm(self, strs: List[str], m: int, n: int) -> int: + dp = [[0] * (n + 1) for _ in range(m + 1)] # 创建二维动态规划数组,初始化为0 # 遍历物品 - for str in strs: - ones = str.count('1') - zeros = str.count('0') - # 遍历背包容量且从后向前遍历! + for s in strs: + ones = s.count('1') # 统计字符串中1的个数 + zeros = s.count('0') # 统计字符串中0的个数 + # 遍历背包容量且从后向前遍历 for i in range(m, zeros - 1, -1): for j in range(n, ones - 1, -1): - dp[i][j] = max(dp[i][j], dp[i - zeros][j - ones] + 1) + dp[i][j] = max(dp[i][j], dp[i - zeros][j - ones] + 1) # 状态转移方程 return dp[m][n] + ``` ### Go From e002075a39841cca40e9fc95c1e23fdf4fcf5e42 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 07:52:08 -0500 Subject: [PATCH 108/122] =?UTF-8?q?Update=20=E8=83=8C=E5=8C=85=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E7=90=86=E8=AE=BA=E5=9F=BA=E7=A1=80=E5=AE=8C=E5=85=A8?= =?UTF-8?q?=E8=83=8C=E5=8C=85.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/背包问题理论基础完全背包.md | 91 +++++++++++++++++++--------- 1 file changed, 63 insertions(+), 28 deletions(-) diff --git a/problems/背包问题理论基础完全背包.md b/problems/背包问题理论基础完全背包.md index e927aa20..2dc45fdb 100644 --- a/problems/背包问题理论基础完全背包.md +++ b/problems/背包问题理论基础完全背包.md @@ -222,44 +222,79 @@ private static void testCompletePackAnotherWay(){ Python: - +先遍历物品,再遍历背包(无参版) ```python -# 先遍历物品,再遍历背包 -def test_complete_pack1(): +def test_CompletePack(): weight = [1, 3, 4] value = [15, 20, 30] - bag_weight = 4 - - dp = [0]*(bag_weight + 1) - - for i in range(len(weight)): - for j in range(weight[i], bag_weight + 1): + bagWeight = 4 + dp = [0] * (bagWeight + 1) + for i in range(len(weight)): # 遍历物品 + for j in range(weight[i], bagWeight + 1): # 遍历背包容量 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) - - print(dp[bag_weight]) + print(dp[bagWeight]) -# 先遍历背包,再遍历物品 -def test_complete_pack2(): - weight = [1, 3, 4] - value = [15, 20, 30] - bag_weight = 4 +test_CompletePack() - dp = [0]*(bag_weight + 1) - - for j in range(bag_weight + 1): - for i in range(len(weight)): - if j >= weight[i]: dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) - - print(dp[bag_weight]) - - -if __name__ == '__main__': - test_complete_pack1() - test_complete_pack2() ``` +先遍历物品,再遍历背包(有参版) +```python +def test_CompletePack(weight, value, bagWeight): + dp = [0] * (bagWeight + 1) + for i in range(len(weight)): # 遍历物品 + for j in range(weight[i], bagWeight + 1): # 遍历背包容量 + dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) + return dp[bagWeight] + +if __name__ == "__main__": + weight = [1, 3, 4] + value = [15, 20, 30] + bagWeight = 4 + result = test_CompletePack(weight, value, bagWeight) + print(result) + +``` +先遍历背包,再遍历物品(无参版) +```python +def test_CompletePack(): + weight = [1, 3, 4] + value = [15, 20, 30] + bagWeight = 4 + + dp = [0] * (bagWeight + 1) + + for j in range(bagWeight + 1): # 遍历背包容量 + for i in range(len(weight)): # 遍历物品 + if j - weight[i] >= 0: + dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) + + print(dp[bagWeight]) + +test_CompletePack() +``` + +先遍历背包,再遍历物品(有参版) +```python +def test_CompletePack(weight, value, bagWeight): + dp = [0] * (bagWeight + 1) + for j in range(bagWeight + 1): # 遍历背包容量 + for i in range(len(weight)): # 遍历物品 + if j - weight[i] >= 0: + dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) + return dp[bagWeight] + + +if __name__ == "__main__": + weight = [1, 3, 4] + value = [15, 20, 30] + bagWeight = 4 + result = test_CompletePack(weight, value, bagWeight) + print(result) + +``` Go: ```go From ea98e43831f3f735bc38b0e5a55f52fe38c5cdd5 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 07:53:16 -0500 Subject: [PATCH 109/122] =?UTF-8?q?Update=20=E8=83=8C=E5=8C=85=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E7=90=86=E8=AE=BA=E5=9F=BA=E7=A1=80=E5=AE=8C=E5=85=A8?= =?UTF-8?q?=E8=83=8C=E5=8C=85.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/背包问题理论基础完全背包.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/problems/背包问题理论基础完全背包.md b/problems/背包问题理论基础完全背包.md index 2dc45fdb..a559a512 100644 --- a/problems/背包问题理论基础完全背包.md +++ b/problems/背包问题理论基础完全背包.md @@ -222,6 +222,9 @@ private static void testCompletePackAnotherWay(){ Python: + + + 先遍历物品,再遍历背包(无参版) ```python def test_CompletePack(): From d7eac13627675be325ca50ce7fa6370b3c589936 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 10:13:13 -0500 Subject: [PATCH 110/122] =?UTF-8?q?Update=200377.=E7=BB=84=E5=90=88?= =?UTF-8?q?=E6=80=BB=E5=92=8C=E2=85=A3.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0377.组合总和Ⅳ.md | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/problems/0377.组合总和Ⅳ.md b/problems/0377.组合总和Ⅳ.md index ee659723..bb847489 100644 --- a/problems/0377.组合总和Ⅳ.md +++ b/problems/0377.组合总和Ⅳ.md @@ -175,22 +175,36 @@ class Solution { ``` Python: +卡哥版 +```python +class Solution: + def combinationSum4(self, nums: List[int], target: int) -> int: + dp = [0] * (target + 1) + dp[0] = 1 + for i in range(1, target + 1): # 遍历背包 + for j in range(len(nums)): # 遍历物品 + if i - nums[j] >= 0: + dp[i] += dp[i - nums[j]] + return dp[target] + +``` +优化版 ```python class Solution: - def combinationSum4(self, nums, target): - dp = [0] * (target + 1) - dp[0] = 1 + def combinationSum4(self, nums: List[int], target: int) -> int: + dp = [0] * (target + 1) # 创建动态规划数组,用于存储组合总数 + dp[0] = 1 # 初始化背包容量为0时的组合总数为1 + + for i in range(1, target + 1): # 遍历背包容量 + for j in nums: # 遍历物品列表 + if i >= j: # 当背包容量大于等于当前物品重量时 + dp[i] += dp[i - j] # 更新组合总数 + + return dp[-1] # 返回背包容量为target时的组合总数 - for i in range(1, target+1): - for j in nums: - if i >= j: - dp[i] += dp[i - j] - return dp[-1] ``` - - Go: ```go func combinationSum4(nums []int, target int) int { From 93952c67e170d338fbc4b017cf10563bf4887c92 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 10:13:29 -0500 Subject: [PATCH 111/122] =?UTF-8?q?Update=200377.=E7=BB=84=E5=90=88?= =?UTF-8?q?=E6=80=BB=E5=92=8C=E2=85=A3.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0377.组合总和Ⅳ.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/problems/0377.组合总和Ⅳ.md b/problems/0377.组合总和Ⅳ.md index bb847489..b6b15386 100644 --- a/problems/0377.组合总和Ⅳ.md +++ b/problems/0377.组合总和Ⅳ.md @@ -175,6 +175,8 @@ class Solution { ``` Python: + + 卡哥版 ```python class Solution: From 245556b1c8e27f14854273383af529379007a360 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:27:54 -0500 Subject: [PATCH 112/122] =?UTF-8?q?Update=200322.=E9=9B=B6=E9=92=B1?= =?UTF-8?q?=E5=85=91=E6=8D=A2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0322.零钱兑换.md | 84 ++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/problems/0322.零钱兑换.md b/problems/0322.零钱兑换.md index 0e3947da..903777ca 100644 --- a/problems/0322.零钱兑换.md +++ b/problems/0322.零钱兑换.md @@ -216,37 +216,75 @@ class Solution { ``` Python: - +先遍历物品 后遍历背包 ```python class Solution: def coinChange(self, coins: List[int], amount: int) -> int: - '''版本一''' - # 初始化 - dp = [float("inf")]*(amount + 1) - dp[0] = 0 - # 遍历物品 - for coin in coins: - # 遍历背包 - for j in range(coin, amount + 1): - dp[j] = min(dp[j], dp[j - coin] + 1) - return dp[amount] if dp[amount] != float("inf") else -1 + dp = [float('inf')] * (amount + 1) # 创建动态规划数组,初始值为正无穷大 + dp[0] = 0 # 初始化背包容量为0时的最小硬币数量为0 + + for coin in coins: # 遍历硬币列表,相当于遍历物品 + for i in range(coin, amount + 1): # 遍历背包容量 + if dp[i - coin] != float('inf'): # 如果dp[i - coin]不是初始值,则进行状态转移 + dp[i] = min(dp[i - coin] + 1, dp[i]) # 更新最小硬币数量 + + if dp[amount] == float('inf'): # 如果最终背包容量的最小硬币数量仍为正无穷大,表示无解 + return -1 + return dp[amount] # 返回背包容量为amount时的最小硬币数量 - def coinChange1(self, coins: List[int], amount: int) -> int: - '''版本二''' - # 初始化 - dp = [float("inf")]*(amount + 1) - dp[0] = 0 - # 遍历物品 - for j in range(1, amount + 1): - # 遍历背包 - for coin in coins: - if j >= coin: - dp[j] = min(dp[j], dp[j - coin] + 1) - return dp[amount] if dp[amount] != float("inf") else -1 ``` +先遍历背包 后遍历物品 +```python +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + dp = [float('inf')] * (amount + 1) # 创建动态规划数组,初始值为正无穷大 + dp[0] = 0 # 初始化背包容量为0时的最小硬币数量为0 + + for i in range(1, amount + 1): # 遍历背包容量 + for j in range(len(coins)): # 遍历硬币列表,相当于遍历物品 + if i - coins[j] >= 0 and dp[i - coins[j]] != float('inf'): # 如果dp[i - coins[j]]不是初始值,则进行状态转移 + dp[i] = min(dp[i - coins[j]] + 1, dp[i]) # 更新最小硬币数量 + + if dp[amount] == float('inf'): # 如果最终背包容量的最小硬币数量仍为正无穷大,表示无解 + return -1 + return dp[amount] # 返回背包容量为amount时的最小硬币数量 + +``` +先遍历物品 后遍历背包(优化版) +```python +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + dp = [float('inf')] * (amount + 1) + dp[0] = 0 + + for coin in coins: + for i in range(coin, amount + 1): + # 更新凑成金额 i 所需的最少硬币数量 + dp[i] = min(dp[i], dp[i - coin] + 1) + + return dp[amount] if dp[amount] != float('inf') else -1 +``` +先遍历背包 后遍历物品(优化版) +```python +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + dp = [float('inf')] * (amount + 1) + dp[0] = 0 + + for i in range(1, amount + 1): # 遍历背包容量 + for coin in coins: # 遍历物品 + if i - coin >= 0: + # 更新凑成金额 i 所需的最少硬币数量 + dp[i] = min(dp[i], dp[i - coin] + 1) + + return dp[amount] if dp[amount] != float('inf') else -1 + + + +``` Go: ```go From beb0a51841fcf308e3044046b93009bf9ea38c82 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:28:14 -0500 Subject: [PATCH 113/122] =?UTF-8?q?Update=200322.=E9=9B=B6=E9=92=B1?= =?UTF-8?q?=E5=85=91=E6=8D=A2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0322.零钱兑换.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/problems/0322.零钱兑换.md b/problems/0322.零钱兑换.md index 903777ca..76ccb234 100644 --- a/problems/0322.零钱兑换.md +++ b/problems/0322.零钱兑换.md @@ -216,6 +216,8 @@ class Solution { ``` Python: + + 先遍历物品 后遍历背包 ```python class Solution: From 0964e16991e4845f8e6dff468d7d0f0ad8a0d0c4 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:32:42 -0500 Subject: [PATCH 114/122] =?UTF-8?q?Update=200322.=E9=9B=B6=E9=92=B1?= =?UTF-8?q?=E5=85=91=E6=8D=A2.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0322.零钱兑换.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/0322.零钱兑换.md b/problems/0322.零钱兑换.md index 76ccb234..67b1a6d9 100644 --- a/problems/0322.零钱兑换.md +++ b/problems/0322.零钱兑换.md @@ -261,7 +261,7 @@ class Solution: dp[0] = 0 for coin in coins: - for i in range(coin, amount + 1): + for i in range(coin, amount + 1): # 进行优化,从能装得下的背包开始计算,则不需要进行比较 # 更新凑成金额 i 所需的最少硬币数量 dp[i] = min(dp[i], dp[i - coin] + 1) From 87e7b9def8de5dc5bb786c9744827e81108859f1 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:58:00 -0500 Subject: [PATCH 115/122] =?UTF-8?q?Update=200279.=E5=AE=8C=E5=85=A8?= =?UTF-8?q?=E5=B9=B3=E6=96=B9=E6=95=B0.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0279.完全平方数.md | 59 +++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/problems/0279.完全平方数.md b/problems/0279.完全平方数.md index f5b23d26..b26f981c 100644 --- a/problems/0279.完全平方数.md +++ b/problems/0279.完全平方数.md @@ -217,36 +217,45 @@ class Solution { Python: +先遍历物品, 再遍历背包 ```python class Solution: def numSquares(self, n: int) -> int: - '''版本一,先遍历背包, 再遍历物品''' - # 初始化 - nums = [i**2 for i in range(1, n + 1) if i**2 <= n] - dp = [10**4]*(n + 1) + dp = [float('inf')] * (n + 1) dp[0] = 0 - # 遍历背包 - for j in range(1, n + 1): - # 遍历物品 - for num in nums: - if j >= num: - dp[j] = min(dp[j], dp[j - num] + 1) - return dp[n] - - def numSquares1(self, n: int) -> int: - '''版本二, 先遍历物品, 再遍历背包''' - # 初始化 - nums = [i**2 for i in range(1, n + 1) if i**2 <= n] - dp = [10**4]*(n + 1) - dp[0] = 0 - # 遍历物品 - for num in nums: - # 遍历背包 - for j in range(num, n + 1): - dp[j] = min(dp[j], dp[j - num] + 1) - return dp[n] -``` + for i in range(1, n + 1): # 遍历背包 + for j in range(1, int(i ** 0.5) + 1): # 遍历物品 + # 更新凑成数字 i 所需的最少完全平方数数量 + dp[i] = min(dp[i], dp[i - j * j] + 1) + + return dp[n] + +``` +其他版本 +```python +class Solution: + def numSquares(self, n: int) -> int: + # 创建动态规划数组,初始值为最大值 + dp = [float('inf')] * (n + 1) + # 初始化已知情况 + dp[0] = 0 + + # 遍历背包容量 + for i in range(1, n + 1): + # 遍历完全平方数作为物品 + j = 1 + while j * j <= i: + # 更新最少完全平方数的数量 + dp[i] = min(dp[i], dp[i - j * j] + 1) + j += 1 + + # 返回结果 + return dp[n] + + + +``` Go: ```go // 版本一,先遍历物品, 再遍历背包 From 45037cec331a3c924a54a24a279e5d0b6d342361 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:05:11 -0500 Subject: [PATCH 116/122] =?UTF-8?q?Update=200279.=E5=AE=8C=E5=85=A8?= =?UTF-8?q?=E5=B9=B3=E6=96=B9=E6=95=B0.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0279.完全平方数.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/problems/0279.完全平方数.md b/problems/0279.完全平方数.md index b26f981c..80b69f0c 100644 --- a/problems/0279.完全平方数.md +++ b/problems/0279.完全平方数.md @@ -231,6 +231,22 @@ class Solution: return dp[n] +``` +先遍历背包, 再遍历物品 +```python +class Solution: + def numSquares(self, n: int) -> int: + dp = [float('inf')] * (n + 1) + dp[0] = 0 + + for i in range(1, int(n ** 0.5) + 1): # 遍历物品 + for j in range(i * i, n + 1): # 遍历背包 + # 更新凑成数字 j 所需的最少完全平方数数量 + dp[j] = min(dp[j - i * i] + 1, dp[j]) + + return dp[n] + + ``` 其他版本 ```python From d8c51b2d362de5a72ec86a7df3f31e486aa8febf Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 5 Jun 2023 03:09:10 -0500 Subject: [PATCH 117/122] =?UTF-8?q?Update=200139.=E5=8D=95=E8=AF=8D?= =?UTF-8?q?=E6=8B=86=E5=88=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0139.单词拆分.md | 56 ++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/problems/0139.单词拆分.md b/problems/0139.单词拆分.md index 230942ef..0f24c71e 100644 --- a/problems/0139.单词拆分.md +++ b/problems/0139.单词拆分.md @@ -337,10 +337,53 @@ class Solution { Python: +回溯 +```python +class Solution: + def backtracking(self, s: str, wordSet: set[str], startIndex: int) -> bool: + # 边界情况:已经遍历到字符串末尾,返回True + if startIndex >= len(s): + return True + + # 遍历所有可能的拆分位置 + for i in range(startIndex, len(s)): + word = s[startIndex:i + 1] # 截取子串 + if word in wordSet and self.backtracking(s, wordSet, i + 1): + # 如果截取的子串在字典中,并且后续部分也可以被拆分成单词,返回True + return True + + # 无法进行有效拆分,返回False + return False + + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + wordSet = set(wordDict) # 转换为哈希集合,提高查找效率 + return self.backtracking(s, wordSet, 0) + +``` +DP(版本一) +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + wordSet = set(wordDict) + n = len(s) + dp = [False] * (n + 1) # dp[i] 表示字符串的前 i 个字符是否可以被拆分成单词 + dp[0] = True # 初始状态,空字符串可以被拆分成单词 + + for i in range(1, n + 1): + for j in range(i): + if dp[j] and s[j:i] in wordSet: + dp[i] = True # 如果 s[0:j] 可以被拆分成单词,并且 s[j:i] 在单词集合中存在,则 s[0:i] 可以被拆分成单词 + break + + return dp[n] + + +``` +DP(版本二) + ```python class Solution: def wordBreak(self, s: str, wordDict: List[str]) -> bool: - '''排列''' dp = [False]*(len(s) + 1) dp[0] = True # 遍历背包 @@ -351,17 +394,6 @@ class Solution: dp[j] = dp[j] or (dp[j - len(word)] and word == s[j - len(word):j]) return dp[len(s)] ``` -```python -class Solution: # 和视频中写法一致(和最上面C++写法一致) - def wordBreak(self, s: str, wordDict: List[str]) -> bool: - dp = [False]*(len(s)+1) - dp[0]=True - for j in range(1,len(s)+1): - for i in range(j): - word = s[i:j] - if word in wordDict and dp[i]: dp[j]=True - return dp[-1] -``` From de7879f26e10077522203378528fc02854d209e0 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 5 Jun 2023 03:12:19 -0500 Subject: [PATCH 118/122] =?UTF-8?q?Update=200139.=E5=8D=95=E8=AF=8D?= =?UTF-8?q?=E6=8B=86=E5=88=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0139.单词拆分.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/problems/0139.单词拆分.md b/problems/0139.单词拆分.md index 0f24c71e..742fe06c 100644 --- a/problems/0139.单词拆分.md +++ b/problems/0139.单词拆分.md @@ -369,8 +369,8 @@ class Solution: dp = [False] * (n + 1) # dp[i] 表示字符串的前 i 个字符是否可以被拆分成单词 dp[0] = True # 初始状态,空字符串可以被拆分成单词 - for i in range(1, n + 1): - for j in range(i): + for i in range(1, n + 1): # 遍历背包 + for j in range(i): # 遍历单词 if dp[j] and s[j:i] in wordSet: dp[i] = True # 如果 s[0:j] 可以被拆分成单词,并且 s[j:i] 在单词集合中存在,则 s[0:i] 可以被拆分成单词 break From bdedd2df0f256b3b5d2c489fb558e92563822ce6 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 5 Jun 2023 07:46:22 -0500 Subject: [PATCH 119/122] =?UTF-8?q?Update=20=E8=83=8C=E5=8C=85=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E7=90=86=E8=AE=BA=E5=9F=BA=E7=A1=80=E5=A4=9A=E9=87=8D?= =?UTF-8?q?=E8=83=8C=E5=8C=85.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/背包问题理论基础多重背包.md | 134 ++++++++++++++++++++------- 1 file changed, 103 insertions(+), 31 deletions(-) diff --git a/problems/背包问题理论基础多重背包.md b/problems/背包问题理论基础多重背包.md index af10dab7..1a856bf5 100644 --- a/problems/背包问题理论基础多重背包.md +++ b/problems/背包问题理论基础多重背包.md @@ -194,55 +194,127 @@ public void testMultiPack2(){ Python: +改变物品数量为01背包格式(无参版) ```python -def test_multi_pack1(): - '''版本一:改变物品数量为01背包格式''' +def test_multi_pack(): weight = [1, 3, 4] value = [15, 20, 30] nums = [2, 3, 2] - bag_weight = 10 + bagWeight = 10 + + # 将数量大于1的物品展开 for i in range(len(nums)): - # 将物品展开数量为1 while nums[i] > 1: weight.append(weight[i]) value.append(value[i]) nums[i] -= 1 - - dp = [0]*(bag_weight + 1) - # 遍历物品 - for i in range(len(weight)): - # 遍历背包 - for j in range(bag_weight, weight[i] - 1, -1): + + dp = [0] * (bagWeight + 1) + for i in range(len(weight)): # 遍历物品 + for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) - - print(" ".join(map(str, dp))) + for j in range(bagWeight + 1): + print(dp[j], end=" ") + print() -def test_multi_pack2(): - '''版本:改变遍历个数''' - weight = [1, 3, 4] - value = [15, 20, 30] - nums = [2, 3, 2] - bag_weight = 10 - - dp = [0]*(bag_weight + 1) - for i in range(len(weight)): - for j in range(bag_weight, weight[i] - 1, -1): - # 以上是01背包,加上遍历个数 - for k in range(1, nums[i] + 1): - if j - k*weight[i] >= 0: - dp[j] = max(dp[j], dp[j - k*weight[i]] + k*value[i]) - - print(" ".join(map(str, dp))) + print(dp[bagWeight]) -if __name__ == '__main__': - test_multi_pack1() - test_multi_pack2() +test_multi_pack() + ``` +改变遍历个数(无参版) +```python +def test_multi_pack(): + weight = [1, 3, 4] + value = [15, 20, 30] + nums = [2, 3, 2] + bagWeight = 10 + dp = [0] * (bagWeight + 1) + + for i in range(len(weight)): # 遍历物品 + for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量 + # 以上为01背包,然后加一个遍历个数 + for k in range(1, nums[i] + 1): # 遍历个数 + if j - k * weight[i] >= 0: + dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i]) + + # 打印一下dp数组 + for j in range(bagWeight + 1): + print(dp[j], end=" ") + print() + + print(dp[bagWeight]) +test_multi_pack() + +``` + + +改变物品数量为01背包格式(有参版) +```python +def test_multi_pack(weight, value, nums, bagWeight): + # 将数量大于1的物品展开 + for i in range(len(nums)): + while nums[i] > 1: + weight.append(weight[i]) + value.append(value[i]) + nums[i] -= 1 + + dp = [0] * (bagWeight + 1) + for i in range(len(weight)): # 遍历物品 + for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量 + dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) + for j in range(bagWeight + 1): + print(dp[j], end=" ") + print() + + print(dp[bagWeight]) + + + + +if __name__ == "__main__": + weight = [1, 3, 4] + value = [15, 20, 30] + nums = [2, 3, 2] + bagWeight = 10 + test_multi_pack(weight, value, nums, bagWeight) +``` + + +改变遍历个数(有参版) +```python +def test_multi_pack(weight, value, nums, bagWeight): + dp = [0] * (bagWeight + 1) + + for i in range(len(weight)): # 遍历物品 + for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量 + # 以上为01背包,然后加一个遍历个数 + for k in range(1, nums[i] + 1): # 遍历个数 + if j - k * weight[i] >= 0: + dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i]) + + # 使用 join 函数打印 dp 数组 + print(' '.join(str(dp[j]) for j in range(bagWeight + 1))) + + print(dp[bagWeight]) + + + + + +if __name__ == "__main__": + weight = [1, 3, 4] + value = [15, 20, 30] + nums = [2, 3, 2] + bagWeight = 10 + test_multi_pack(weight, value, nums, bagWeight) + +``` Go: ```go From c33556c440f24942f94a55a0a2f48a1abb300290 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 5 Jun 2023 07:54:00 -0500 Subject: [PATCH 120/122] =?UTF-8?q?Update=200198.=E6=89=93=E5=AE=B6?= =?UTF-8?q?=E5=8A=AB=E8=88=8D.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0198.打家劫舍.md | 62 ++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/problems/0198.打家劫舍.md b/problems/0198.打家劫舍.md index 6e682ec3..a2fbe8ce 100644 --- a/problems/0198.打家劫舍.md +++ b/problems/0198.打家劫舍.md @@ -166,30 +166,64 @@ class Solution { ``` Python: +1维DP ```python class Solution: def rob(self, nums: List[int]) -> int: - if len(nums) == 0: + if len(nums) == 0: # 如果没有房屋,返回0 return 0 - if len(nums) == 1: + if len(nums) == 1: # 如果只有一个房屋,返回其金额 return nums[0] + + # 创建一个动态规划数组,用于存储最大金额 dp = [0] * len(nums) - dp[0] = nums[0] - dp[1] = max(nums[0], nums[1]) + dp[0] = nums[0] # 将dp的第一个元素设置为第一个房屋的金额 + dp[1] = max(nums[0], nums[1]) # 将dp的第二个元素设置为第一二个房屋中的金额较大者 + + # 遍历剩余的房屋 for i in range(2, len(nums)): - dp[i] = max(dp[i-2]+nums[i], dp[i-1]) - return dp[-1] + # 对于每个房屋,选择抢劫当前房屋和抢劫前一个房屋的最大金额 + dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]) + + return dp[-1] # 返回最后一个房屋中可抢劫的最大金额 ``` +2维DP ```python -class Solution: # 二维dp数组写法 +class Solution: def rob(self, nums: List[int]) -> int: - dp = [[0,0] for _ in range(len(nums))] - dp[0][1] = nums[0] - for i in range(1,len(nums)): - dp[i][0] = max(dp[i-1][1],dp[i-1][0]) - dp[i][1] = dp[i-1][0]+nums[i] - print(dp) - return max(dp[-1]) + if not nums: # 如果没有房屋,返回0 + return 0 + + n = len(nums) + dp = [[0, 0] for _ in range(n)] # 创建二维动态规划数组,dp[i][0]表示不抢劫第i个房屋的最大金额,dp[i][1]表示抢劫第i个房屋的最大金额 + + dp[0][1] = nums[0] # 抢劫第一个房屋的最大金额为第一个房屋的金额 + + for i in range(1, n): + dp[i][0] = max(dp[i-1][0], dp[i-1][1]) # 不抢劫第i个房屋,最大金额为前一个房屋抢劫和不抢劫的最大值 + dp[i][1] = dp[i-1][0] + nums[i] # 抢劫第i个房屋,最大金额为前一个房屋不抢劫的最大金额加上当前房屋的金额 + + return max(dp[n-1][0], dp[n-1][1]) # 返回最后一个房屋中可抢劫的最大金额 + +``` +优化版 +```python +class Solution: + def rob(self, nums: List[int]) -> int: + if not nums: # 如果没有房屋,返回0 + return 0 + + prev_max = 0 # 上一个房屋的最大金额 + curr_max = 0 # 当前房屋的最大金额 + + for num in nums: + temp = curr_max # 临时变量保存当前房屋的最大金额 + curr_max = max(prev_max + num, curr_max) # 更新当前房屋的最大金额 + prev_max = temp # 更新上一个房屋的最大金额 + + return curr_max # 返回最后一个房屋中可抢劫的最大金额 + + ``` Go: ```Go From bc1324292575f7c67c7ed207b0fe5714244a5984 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 5 Jun 2023 07:54:20 -0500 Subject: [PATCH 121/122] =?UTF-8?q?Update=200198.=E6=89=93=E5=AE=B6?= =?UTF-8?q?=E5=8A=AB=E8=88=8D.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0198.打家劫舍.md | 1 + 1 file changed, 1 insertion(+) diff --git a/problems/0198.打家劫舍.md b/problems/0198.打家劫舍.md index a2fbe8ce..084aa301 100644 --- a/problems/0198.打家劫舍.md +++ b/problems/0198.打家劫舍.md @@ -166,6 +166,7 @@ class Solution { ``` Python: + 1维DP ```python class Solution: From 3b3d9c4d11328311f945a420f40a03cdb8a92512 Mon Sep 17 00:00:00 2001 From: jianghongcheng <35664721+jianghongcheng@users.noreply.github.com> Date: Mon, 5 Jun 2023 08:07:53 -0500 Subject: [PATCH 122/122] =?UTF-8?q?Update=200213.=E6=89=93=E5=AE=B6?= =?UTF-8?q?=E5=8A=AB=E8=88=8DII.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/0213.打家劫舍II.md | 101 +++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 24 deletions(-) diff --git a/problems/0213.打家劫舍II.md b/problems/0213.打家劫舍II.md index 6395f3a8..3f532a41 100644 --- a/problems/0213.打家劫舍II.md +++ b/problems/0213.打家劫舍II.md @@ -130,40 +130,93 @@ class Solution { ``` Python: + ```Python class Solution: def rob(self, nums: List[int]) -> int: - #在198入门级的打家劫舍问题上分两种情况考虑 - #一是不偷第一间房,二是不偷最后一间房 - if len(nums)==1:#题目中提示nums.length>=1,所以不需要考虑len(nums)==0的情况 + if len(nums) == 0: + return 0 + if len(nums) == 1: return nums[0] - val1=self.roblist(nums[1:])#不偷第一间房 - val2=self.roblist(nums[:-1])#不偷最后一间房 - return max(val1,val2) + + result1 = self.robRange(nums, 0, len(nums) - 2) # 情况二 + result2 = self.robRange(nums, 1, len(nums) - 1) # 情况三 + return max(result1, result2) + # 198.打家劫舍的逻辑 + def robRange(self, nums: List[int], start: int, end: int) -> int: + if end == start: + return nums[start] + + prev_max = nums[start] + curr_max = max(nums[start], nums[start + 1]) + + for i in range(start + 2, end + 1): + temp = curr_max + curr_max = max(prev_max + nums[i], curr_max) + prev_max = temp + + return curr_max - def roblist(self,nums): - l=len(nums) - dp=[0]*l - dp[0]=nums[0] - for i in range(1,l): - if i==1: - dp[i]=max(dp[i-1],nums[i]) - else: - dp[i]=max(dp[i-1],dp[i-2]+nums[i]) - return dp[-1] ``` +2维DP ```python -class Solution: # 二维dp数组写法 +class Solution: def rob(self, nums: List[int]) -> int: - if len(nums)<3: return max(nums) - return max(self.default(nums[:-1]),self.default(nums[1:])) - def default(self,nums): - dp = [[0,0] for _ in range(len(nums))] + if len(nums) < 3: + return max(nums) + + # 情况二:不抢劫第一个房屋 + result1 = self.robRange(nums[:-1]) + + # 情况三:不抢劫最后一个房屋 + result2 = self.robRange(nums[1:]) + + return max(result1, result2) + + def robRange(self, nums): + dp = [[0, 0] for _ in range(len(nums))] dp[0][1] = nums[0] - for i in range(1,len(nums)): - dp[i][0] = max(dp[i-1]) - dp[i][1] = dp[i-1][0] + nums[i] + + for i in range(1, len(nums)): + dp[i][0] = max(dp[i - 1]) + dp[i][1] = dp[i - 1][0] + nums[i] + return max(dp[-1]) + + + +``` + +优化版 +```python +class Solution: + def rob(self, nums: List[int]) -> int: + if not nums: # 如果没有房屋,返回0 + return 0 + + if len(nums) == 1: # 如果只有一个房屋,返回该房屋的金额 + return nums[0] + + # 情况二:不抢劫第一个房屋 + prev_max = 0 # 上一个房屋的最大金额 + curr_max = 0 # 当前房屋的最大金额 + for num in nums[1:]: + temp = curr_max # 临时变量保存当前房屋的最大金额 + curr_max = max(prev_max + num, curr_max) # 更新当前房屋的最大金额 + prev_max = temp # 更新上一个房屋的最大金额 + result1 = curr_max + + # 情况三:不抢劫最后一个房屋 + prev_max = 0 # 上一个房屋的最大金额 + curr_max = 0 # 当前房屋的最大金额 + for num in nums[:-1]: + temp = curr_max # 临时变量保存当前房屋的最大金额 + curr_max = max(prev_max + num, curr_max) # 更新当前房屋的最大金额 + prev_max = temp # 更新上一个房屋的最大金额 + result2 = curr_max + + return max(result1, result2) + ``` Go: