diff --git a/README.md b/README.md index daef7c02..60aa4fd5 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ > 1. **介绍**:本项目是一套完整的刷题计划,旨在帮助大家少走弯路,循序渐进学算法,[关注作者](#关于作者) > 2. **PDF版本** : [「代码随想录」算法精讲 PDF 版本](https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ) 。 +> 3. **刷题顺序** : README已经将刷题顺序排好了,按照顺序一道一道刷就可以。 > 3. **学习社区** : 一起学习打卡/面试技巧/如何选择offer/大厂内推/职场规则/简历修改/技术分享/程序人生。欢迎加入[「代码随想录」学习社区](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) 。 > 4. **提交代码**:本项目统一使用C++语言进行讲解,但已经有Java、Python、Go、JavaScript等等多语言版本,感谢[这里的每一位贡献者](https://github.com/youngyangyang04/leetcode-master/graphs/contributors),如果你也想贡献代码点亮你的头像,[点击这里](https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A)了解提交代码的方式。 > 5. **转载须知** :以下所有文章皆为我([程序员Carl](https://github.com/youngyangyang04))的原创。引用本项目文章请注明出处,发现恶意抄袭或搬运,会动用法律武器维护自己的权益。让我们一起维护一个良好的技术创作环境! @@ -41,7 +42,7 @@ 对于刷题,我们都是想用最短的时间**按照循序渐进的难度顺序把经典题目都做一遍**,这样效率才是最高的! -所以我整理了leetcode刷题攻略:一个超级详细的刷题顺序,**每道题目都是我精心筛选,都是经典题目高频面试题**,大家只要按照这个顺序刷就可以了,**你没看错,就是题目顺序都排好了,文章顺序就是刷题顺序!挨个刷就可以,不用自己再去题海里选题了!** +所以我整理了leetcode刷题攻略:一个超级详细的刷题顺序,**每道题目都是我精心筛选,都是经典题目高频面试题**,大家只要按照这个顺序刷就可以了,**你没看错,README已经把题目顺序都排好了,文章顺序就是刷题顺序!挨个刷就可以,不用自己再去题海里选题了!** 而且每道题目我都写了的详细题解(图文并茂,难点配有视频),力扣上我的题解都是排在对应题目的首页,质量是有目共睹的。 @@ -117,7 +118,7 @@ (持续更新中.....) -## 备战秋招 +## 知识星球精选 1. [选择方向的时候,我也迷茫了](https://mp.weixin.qq.com/s/ZCzFiAHZHLqHPLJQXNm75g) 2. [刷题就用库函数了,怎么了?](https://mp.weixin.qq.com/s/6K3_OSaudnHGq2Ey8vqYfg) @@ -125,6 +126,10 @@ 4. [马上秋招了,慌得很!](https://mp.weixin.qq.com/s/7q7W8Cb2-a5U5atZdOnOFA) 5. [Carl看了上百份简历,总结了这些!](https://mp.weixin.qq.com/s/sJa87MZD28piCOVMFkIbwQ) 6. [面试中遇到了发散性问题.....](https://mp.weixin.qq.com/s/SSonDxi2pjkSVwHNzZswng) +7. [英语到底重不重要!](https://mp.weixin.qq.com/s/1PRZiyF_-TVA-ipwDNjdKw) +8. [计算机专业要不要读研!](https://mp.weixin.qq.com/s/c9v1L3IjqiXtkNH7sOMAdg) +9. [秋招和提前批都越来越提前了....](https://mp.weixin.qq.com/s/SNFiRDx8CKyjhTPlys6ywQ) + ## 数组 diff --git a/problems/0017.电话号码的字母组合.md b/problems/0017.电话号码的字母组合.md index aefee698..1562052c 100644 --- a/problems/0017.电话号码的字母组合.md +++ b/problems/0017.电话号码的字母组合.md @@ -342,6 +342,46 @@ class Solution: Go: + +> 主要在于递归中传递下一个数字 + +```go +func letterCombinations(digits string) []string { + lenth:=len(digits) + if lenth==0 ||lenth>4{ + return nil + } + digitsMap:= [10]string{ + "", // 0 + "", // 1 + "abc", // 2 + "def", // 3 + "ghi", // 4 + "jkl", // 5 + "mno", // 6 + "pqrs", // 7 + "tuv", // 8 + "wxyz", // 9 + } + res:=make([]string,0) + recursion("",digits,0,digitsMap,&res) + return res +} +func recursion(tempString ,digits string, Index int,digitsMap [10]string, res *[]string) {//index表示第几个数字 + if len(tempString)==len(digits){//终止条件,字符串长度等于digits的长度 + *res=append(*res,tempString) + return + } + tmpK:=digits[Index]-'0' // 将index指向的数字转为int(确定下一个数字) + letter:=digitsMap[tmpK]// 取数字对应的字符集 + for i:=0;i \ No newline at end of file +
diff --git a/problems/0037.解数独.md b/problems/0037.解数独.md index 4eb60704..e43708b8 100644 --- a/problems/0037.解数独.md +++ b/problems/0037.解数独.md @@ -321,6 +321,59 @@ class Solution: backtrack(board) ``` +Python3: + +```python3 +class Solution: + def __init__(self) -> None: + self.board = [] + + def isValid(self, row: int, col: int, target: int) -> bool: + for idx in range(len(self.board)): + # 同列是否重复 + if self.board[idx][col] == str(target): + return False + # 同行是否重复 + if self.board[row][idx] == str(target): + return False + # 9宫格里是否重复 + box_row, box_col = (row // 3) * 3 + idx // 3, (col // 3) * 3 + idx % 3 + if self.board[box_row][box_col] == str(target): + return False + return True + + def getPlace(self) -> List[int]: + for row in range(len(self.board)): + for col in range(len(self.board)): + if self.board[row][col] == ".": + return [row, col] + return [-1, -1] + + def isSolved(self) -> bool: + row, col = self.getPlace() # 找个空位置 + + if row == -1 and col == -1: # 没有空位置,棋盘被填满的 + return True + + for i in range(1, 10): + if self.isValid(row, col, i): # 检查这个空位置放i,是否合适 + self.board[row][col] = str(i) # 放i + if self.isSolved(): # 合适,立刻返回, 填下一个空位置。 + return True + self.board[row][col] = "." # 不合适,回溯 + + return False # 空位置没法解决 + + def solveSudoku(self, board: List[List[str]]) -> None: + """ + Do not return anything, modify board in-place instead. + """ + if board is None or len(board) == 0: + return + self.board = board + self.isSolved() +``` + Go: Javascript: diff --git a/problems/0039.组合总和.md b/problems/0039.组合总和.md index e3e4a117..ba8128b5 100644 --- a/problems/0039.组合总和.md +++ b/problems/0039.组合总和.md @@ -286,6 +286,39 @@ class Solution: ``` Go: + +> 主要在于递归中传递下一个数字 + +```go +func combinationSum(candidates []int, target int) [][]int { + var trcak []int + var res [][]int + backtracking(0,0,target,candidates,trcak,&res) + return res +} +func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int){ + //终止条件 + if sum==target{ + tmp:=make([]int,len(trcak)) + copy(tmp,trcak)//拷贝 + *res=append(*res,tmp)//放入结果集 + return + } + if sum>target{return} + //回溯 + for i:=startIndex;i 主要在于如何在回溯中去重 + +```go +func combinationSum2(candidates []int, target int) [][]int { + var trcak []int + var res [][]int + var history map[int]bool + history=make(map[int]bool) + sort.Ints(candidates) + backtracking(0,0,target,candidates,trcak,&res,history) + return res +} +func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int,history map[int]bool){ + //终止条件 + if sum==target{ + tmp:=make([]int,len(trcak)) + copy(tmp,trcak)//拷贝 + *res=append(*res,tmp)//放入结果集 + return + } + if sum>target{return} + //回溯 + // used[i - 1] == true,说明同一树支candidates[i - 1]使用过 + // used[i - 1] == false,说明同一树层candidates[i - 1]使用过 + for i:=startIndex;i0&&candidates[i]==candidates[i-1]&&history[i-1]==false{ + continue + } + //更新路径集合和sum + trcak=append(trcak,candidates[i]) + sum+=candidates[i] + history[i]=true + //递归 + backtracking(i+1,sum,target,candidates,trcak,res,history) + //回溯 + trcak=trcak[:len(trcak)-1] + sum-=candidates[i] + history[i]=false + } +} +``` javaScript: ```js diff --git a/problems/0046.全排列.md b/problems/0046.全排列.md index 06367851..30c3374b 100644 --- a/problems/0046.全排列.md +++ b/problems/0046.全排列.md @@ -244,31 +244,35 @@ func backtrack(nums,pathNums []int,used []bool){ } } +``` + Javascript: -```javascript +```js +/** + * @param {number[]} nums + * @return {number[][]} + */ var permute = function(nums) { - let result = [] - let path = [] - function backtracing(used) { - if(path.length === nums.length) { - result.push(path.slice(0)) - return + const res = [], path = []; + backtracking(nums, nums.length, []); + return res; + + function backtracking(n, k, used) { + if(path.length === k) { + res.push(Array.from(path)); + return; } - for(let i = 0; i < nums.length; i++) { - if(used[nums[i]]) { - continue - } - used[nums[i]] = true - path.push(nums[i]) - backtracing(used) - path.pop() - used[nums[i]] = false + for (let i = 0; i < k; i++ ) { + if(used[i]) continue; + path.push(n[i]); + used[i] = true; // 同支 + backtracking(n, k, used); + path.pop(); + used[i] = false; } } - backtracing([]) - return result }; ``` diff --git a/problems/0051.N皇后.md b/problems/0051.N皇后.md index 5242fce2..fd2c7d0f 100644 --- a/problems/0051.N皇后.md +++ b/problems/0051.N皇后.md @@ -353,14 +353,6 @@ class Solution { } ``` -## 其他语言版本 - - -Java: - - -Python: - Go: ```Go diff --git a/problems/0062.不同路径.md b/problems/0062.不同路径.md index 47cb41af..50e70b3e 100644 --- a/problems/0062.不同路径.md +++ b/problems/0062.不同路径.md @@ -279,9 +279,7 @@ Python: ```python class Solution: # 动态规划 def uniquePaths(self, m: int, n: int) -> int: - dp = [[0 for i in range(n)] for j in range(m)] - for i in range(m): dp[i][0] = 1 - for j in range(n): dp[0][j] = 1 + dp = [[1 for i in range(n)] for j in range(m)] for i in range(1, m): for j in range(1, n): dp[i][j] = dp[i][j - 1] + dp[i - 1][j] diff --git a/problems/0063.不同路径II.md b/problems/0063.不同路径II.md index 52f00322..a61ffd02 100644 --- a/problems/0063.不同路径II.md +++ b/problems/0063.不同路径II.md @@ -232,6 +232,38 @@ class Solution: return dp[-1][-1] ``` +```python +class Solution: + """ + 使用一维dp数组 + """ + + def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int: + m, n = len(obstacleGrid), len(obstacleGrid[0]) + + # 初始化dp数组 + # 该数组缓存当前行 + curr = [0] * n + for j in range(n): + if obstacleGrid[0][j] == 1: + break + curr[j] = 1 + + for i in range(1, m): # 从第二行开始 + for j in range(n): # 从第一列开始,因为第一列可能有障碍物 + # 有障碍物处无法通行,状态就设成0 + 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] + + return curr[n - 1] +``` + Go: diff --git a/problems/0093.复原IP地址.md b/problems/0093.复原IP地址.md index a8b9a215..40ad7684 100644 --- a/problems/0093.复原IP地址.md +++ b/problems/0093.复原IP地址.md @@ -338,6 +338,46 @@ class Solution(object): return ans``` ``` +```python3 +class Solution: + def __init__(self) -> None: + self.s = "" + self.res = [] + + def isVaild(self, s: str) -> bool: + if len(s) > 1 and s[0] == "0": + return False + + if 0 <= int(s) <= 255: + return True + + return False + + def backTrack(self, path: List[str], start: int) -> None: + if start == len(self.s) and len(path) == 4: + self.res.append(".".join(path)) + return + + for end in range(start + 1, len(self.s) + 1): + # 剪枝 + # 保证切割完,s没有剩余的字符。 + if len(self.s) - end > 3 * (4 - len(path) - 1): + continue + if self.isVaild(self.s[start:end]): + # 在参数处,更新状态,实则创建一个新的变量 + # 不会影响当前的状态,当前的path变量没有改变 + # 因此递归完不用path.pop() + self.backTrack(path + [self.s[start:end]], end) + + def restoreIpAddresses(self, s: str) -> List[str]: + # prune + if len(s) > 3 * 4: + return [] + self.s = s + self.backTrack([], 0) + return self.res +``` + JavaScript: ```js @@ -367,6 +407,47 @@ var restoreIpAddresses = function(s) { } }; ``` +Go: +> 回溯(对于前导 0的IP(特别注意s[startIndex]=='0'的判断,不应该写成s[startIndex]==0,因为s截取出来不是数字)) + +```go +func restoreIpAddresses(s string) []string { + var res,path []string + backTracking(s,path,0,&res) + return res +} +func backTracking(s string,path []string,startIndex int,res *[]string){ + //终止条件 + if startIndex==len(s)&&len(path)==4{ + tmpIpString:=path[0]+"."+path[1]+"."+path[2]+"."+path[3] + *res=append(*res,tmpIpString) + } + for i:=startIndex;i1&&s[startIndex]=='0'{//对于前导 0的IP(特别注意s[startIndex]=='0'的判断,不应该写成s[startIndex]==0,因为s截取出来不是数字) + return false + } + if checkInt>255{ + return false + } + return true +} + +``` + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) diff --git a/problems/0098.验证二叉搜索树.md b/problems/0098.验证二叉搜索树.md index eb877abb..e38c5ade 100644 --- a/problems/0098.验证二叉搜索树.md +++ b/problems/0098.验证二叉搜索树.md @@ -377,6 +377,75 @@ func isBST(root *TreeNode, min, max int) bool { } ``` +JavaScript版本 + +> 辅助数组解决 + +```javascript +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {boolean} + */ +var isValidBST = function (root) { + let arr = []; + const buildArr = (root) => { + if (root) { + buildArr(root.left); + arr.push(root.val); + buildArr(root.right); + } + } + buildArr(root); + for (let i = 1; i < arr.length; ++i) { + if (arr[i] <= arr[i - 1]) + return false; + } + return true; +}; +``` + +> 递归中解决 + +```javascript +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {boolean} + */ +let pre = null; +var isValidBST = function (root) { + let pre = null; + const inOrder = (root) => { + if (root === null) + return true; + let left = inOrder(root.left); + + if (pre !== null && pre.val >= root.val) + return false; + pre = root; + + let right = inOrder(root.right); + return left && right; + } + return inOrder(root); +}; +``` + ----------------------- diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md index 89d0dda7..341f4ba5 100644 --- a/problems/0102.二叉树的层序遍历.md +++ b/problems/0102.二叉树的层序遍历.md @@ -98,7 +98,7 @@ class Solution: out_list = [] while quene: - length = len(queue) # 这里一定要先求出队列的长度,不能用range(len(queue)),因为queue长度是变化的 + length = len(queue) in_list = [] for _ in range(length): curnode = queue.pop(0) # (默认移除列表最后一个元素)这里需要移除队列最头上的那个 @@ -627,6 +627,27 @@ public: } }; ``` +python代码: + +```python +class Solution: + def largestValues(self, root: TreeNode) -> List[int]: + if root is None: + return [] + queue = [root] + out_list = [] + while queue: + length = len(queue) + in_list = [] + for _ in range(length): + curnode = queue.pop(0) + in_list.append(curnode.val) + if curnode.left: queue.append(curnode.left) + if curnode.right: queue.append(curnode.right) + out_list.append(max(in_list)) + return out_list +``` + javascript代码: ```javascript @@ -712,6 +733,42 @@ public: }; ``` +python代码: + +```python +# 层序遍历解法 +class Solution: + def connect(self, root: 'Node') -> 'Node': + if not root: + return None + queue = [root] + while queue: + n = len(queue) + for i in range(n): + node = queue.pop(0) + if node.left: + queue.append(node.left) + if node.right: + queue.append(node.right) + if i == n - 1: + break + node.next = queue[0] + return root + +# 链表解法 +class Solution: + def connect(self, root: 'Node') -> 'Node': + first = root + while first: + cur = first + while cur: # 遍历每一层的节点 + if cur.left: cur.left.next = cur.right # 找左节点的next + if cur.right and cur.next: cur.right.next = cur.next.left # 找右节点的next + cur = cur.next # cur同层移动到下一节点 + first = first.left # 从本层扩展到下一层 + return root +``` + ## 117.填充每个节点的下一个右侧节点指针II 题目地址:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/ @@ -753,7 +810,48 @@ public: } }; ``` +python代码: +```python +# 层序遍历解法 +class Solution: + def connect(self, root: 'Node') -> 'Node': + if not root: + return None + queue = [root] + while queue: # 遍历每一层 + length = len(queue) + tail = None # 每一层维护一个尾节点 + for i in range(length): # 遍历当前层 + curnode = queue.pop(0) + if tail: + tail.next = curnode # 让尾节点指向当前节点 + tail = curnode # 让当前节点成为尾节点 + if curnode.left : queue.append(curnode.left) + if curnode.right: queue.append(curnode.right) + return root + +# 链表解法 +class Solution: + def connect(self, root: 'Node') -> 'Node': + if not root: + return None + first = root + while first: # 遍历每一层 + dummyHead = Node(None) # 为下一行创建一个虚拟头节点,相当于下一行所有节点链表的头结点(每一层都会创建); + tail = dummyHead # 为下一行维护一个尾节点指针(初始化是虚拟节点) + cur = first + while cur: # 遍历当前层的节点 + if cur.left: # 链接下一行的节点 + tail.next = cur.left + tail = tail.next + if cur.right: + tail.next = cur.right + tail = tail.next + cur = cur.next # cur同层移动到下一节点 + first = dummyHead.next # 此处为换行操作,更新到下一行 + return root +``` ## 总结 diff --git a/problems/0106.从中序与后序遍历序列构造二叉树.md b/problems/0106.从中序与后序遍历序列构造二叉树.md index 600a38e0..4c5a70a0 100644 --- a/problems/0106.从中序与后序遍历序列构造二叉树.md +++ b/problems/0106.从中序与后序遍历序列构造二叉树.md @@ -775,6 +775,20 @@ var buildTree = function(inorder, postorder) { }; ``` +从前序与中序遍历序列构造二叉树 + +```javascript +var buildTree = function(preorder, inorder) { + if(!preorder.length) + return null; + let root = new TreeNode(preorder[0]); + let mid = inorder.findIndex((number) => number === root.val); + root.left = buildTree(preorder.slice(1, mid + 1), inorder.slice(0, mid)); + root.right = buildTree(preorder.slice(mid + 1, preorder.length), inorder.slice(mid + 1, inorder.length)); + return root; +}; +``` + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) diff --git a/problems/0108.将有序数组转换为二叉搜索树.md b/problems/0108.将有序数组转换为二叉搜索树.md index 2692be47..b8861b24 100644 --- a/problems/0108.将有序数组转换为二叉搜索树.md +++ b/problems/0108.将有序数组转换为二叉搜索树.md @@ -54,7 +54,7 @@ 如下两棵树,都是这个数组的平衡二叉搜索树: -![108.将有序数组转换为二叉搜索树](https://code-thinking.cdn.bcebos.com/pics/108.%E5%B0%86%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E8%BD%AC%E6%8D%A2%E4%B8%BA%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.png) +![108.将有序数组转换为二叉搜索树](https://code-thinking.cdn.bcebos.com/pics/108.将有序数组转换为二叉搜索树.png) 如果要分割的数组长度为偶数的时候,中间元素为两个,是取左边元素 就是树1,取右边元素就是树2。 @@ -258,6 +258,29 @@ class Solution: Go: +> 递归(隐含回溯) + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + //递归(隐含回溯) +func sortedArrayToBST(nums []int) *TreeNode { + if len(nums)==0{return nil}//终止条件,最后数组为空则可以返回 + root:=&TreeNode{nums[len(nums)/2],nil,nil}//按照BSL的特点,从中间构造节点 + root.Left=sortedArrayToBST(nums[:len(nums)/2])//数组的左边为左子树 + root.Right=sortedArrayToBST(nums[len(nums)/2+1:])//数字的右边为右子树 + return root +} +``` + + + ----------------------- diff --git a/problems/0112.路径总和.md b/problems/0112.路径总和.md index 54f79d1d..d798ca90 100644 --- a/problems/0112.路径总和.md +++ b/problems/0112.路径总和.md @@ -117,7 +117,7 @@ if (cur->left) { // 左 } if (cur->right) { // 右 count -= cur->right->val; - if (traversal(cur->right, count - cur->right->val)) return true; + if (traversal(cur->right, count)) return true; count += cur->right->val; } return false; diff --git a/problems/0131.分割回文串.md b/problems/0131.分割回文串.md index 9d23fd13..43453409 100644 --- a/problems/0131.分割回文串.md +++ b/problems/0131.分割回文串.md @@ -312,6 +312,50 @@ class Solution: ``` Go: +> 注意切片(go切片是披着值类型外衣的引用类型) + +```go +func partition(s string) [][]string { + var tmpString []string//切割字符串集合 + var res [][]string//结果集合 + backTracking(s,tmpString,0,&res) + return res +} +func backTracking(s string,tmpString []string,startIndex int,res *[][]string){ + if startIndex==len(s){//到达字符串末尾了 + //进行一次切片拷贝,怕之后的操作影响tmpString切片内的值 + t := make([]string, len(tmpString)) + copy(t, tmpString) + *res=append(*res,t) + } + for i:=startIndex;i ```C++ // 版本一 @@ -203,6 +203,7 @@ public: return s; } + // 当然这里的主函数reverseWords写的有一些冗余的,可以精简一些,精简之后的主函数为: /* 主函数简单写法 string reverseWords(string s) { removeExtraSpaces(s); @@ -220,25 +221,8 @@ public: }; ``` -当然这里的主函数reverseWords写的有一些冗余的,可以精简一些,精简之后的主函数为: - -```C++ -// 注意这里仅仅是主函数,其他函数和版本一一致 -string reverseWords(string s) { - removeExtraSpaces(s); - reverse(s, 0, s.size() - 1); - for(int i = 0; i < s.size(); i++) { - int j = i; - // 查找单词间的空格,翻转单词 - while(j < s.size() && s[j] != ' ') j++; - reverse(s, i, j - 1); - i = j; - } - return s; -} -``` - - +效率: + @@ -316,7 +300,57 @@ class Solution { } ``` -Python: + +```Python3 +class Solution: + #1.去除多余的空格 + def trim_spaces(self,s): + n=len(s) + left=0 + right=n-1 + + while left<=right and s[left]==' ': #去除开头的空格 + left+=1 + while left<=right and s[right]==' ': #去除结尾的空格 + right=right-1 + tmp=[] + while left<=right: #去除单词中间多余的空格 + if s[left]!=' ': + tmp.append(s[left]) + elif tmp[-1]!=' ': #当前位置是空格,但是相邻的上一个位置不是空格,则该空格是合理的 + tmp.append(s[left]) + left+=1 + return tmp +#2.翻转字符数组 + def reverse_string(self,nums,left,right): + while left ListNode: + + def reverse(pre,cur): + if not cur: + return pre + + tmp = cur.next + cur.next = pre + + return reverse(cur,tmp) + + return reverse(None,head) + +``` + + + Go: ```go diff --git a/problems/0209.长度最小的子数组.md b/problems/0209.长度最小的子数组.md index 90280451..42687514 100644 --- a/problems/0209.长度最小的子数组.md +++ b/problems/0209.长度最小的子数组.md @@ -109,7 +109,7 @@ public: }; ``` -时间复杂度:$O(n)$ +时间复杂度:$O(n)$ 空间复杂度:$O(1)$ **一些录友会疑惑为什么时间复杂度是O(n)**。 @@ -118,8 +118,8 @@ public: ## 相关题目推荐 -* 904.水果成篮 -* 76.最小覆盖子串 +* [904.水果成篮](https://leetcode-cn.com/problems/fruit-into-baskets/) +* [76.最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring/) diff --git a/problems/0216.组合总和III.md b/problems/0216.组合总和III.md index 9f75b23d..67e67ad0 100644 --- a/problems/0216.组合总和III.md +++ b/problems/0216.组合总和III.md @@ -284,6 +284,37 @@ class Solution: Go: + +> 回溯+减枝 + +```go +func combinationSum3(k int, n int) [][]int { + var track []int// 遍历路径 + var result [][]int// 存放结果集 + backTree(n,k,1,&track,&result) + return result +} +func backTree(n,k,startIndex int,track *[]int,result *[][]int){ + if len(*track)==k{ + var sum int + tmp:=make([]int,k) + for k,v:=range *track{ + sum+=v + tmp[k]=v + } + if sum==n{ + *result=append(*result,tmp) + } + return + } + for i:=startIndex;i<=9-(k-len(*track))+1;i++{//减枝(k-len(*track)表示还剩多少个可填充的元素) + *track=append(*track,i)//记录路径 + backTree(n,k,i+1,track,result)//递归 + *track=(*track)[:len(*track)-1]//回溯 + } +} +``` + javaScript: ```js diff --git a/problems/0222.完全二叉树的节点个数.md b/problems/0222.完全二叉树的节点个数.md index 998e22f3..ec68b6c6 100644 --- a/problems/0222.完全二叉树的节点个数.md +++ b/problems/0222.完全二叉树的节点个数.md @@ -335,6 +335,30 @@ func countNodes(root *TreeNode) int { } ``` +利用完全二叉树特性的递归解法 +```go +func countNodes(root *TreeNode) int { + if root == nil { + return 0 + } + leftH, rightH := 0, 0 + leftNode := root.Left + rightNode := root.Right + for leftNode != nil { + leftNode = leftNode.Left + leftH++ + } + for rightNode != nil { + rightNode = rightNode.Right + rightH++ + } + if leftH == rightH { + return (2 << leftH) - 1 + } + return countNodes(root.Left) + countNodes(root.Right) + 1 +} +``` + JavaScript: diff --git a/problems/0232.用栈实现队列.md b/problems/0232.用栈实现队列.md index c2af71f7..be6e1df6 100644 --- a/problems/0232.用栈实现队列.md +++ b/problems/0232.用栈实现队列.md @@ -15,10 +15,10 @@ https://leetcode-cn.com/problems/implement-queue-using-stacks/ 使用栈实现队列的下列操作: -push(x) -- 将一个元素放入队列的尾部。 -pop() -- 从队列首部移除元素。 -peek() -- 返回队列首部的元素。 -empty() -- 返回队列是否为空。 +push(x) -- 将一个元素放入队列的尾部。 +pop() -- 从队列首部移除元素。 +peek() -- 返回队列首部的元素。 +empty() -- 返回队列是否为空。 示例: @@ -46,18 +46,18 @@ queue.empty(); // 返回 false 下面动画模拟以下队列的执行过程如下: -执行语句: -queue.push(1); -queue.push(2); -queue.pop(); **注意此时的输出栈的操作** -queue.push(3); -queue.push(4); -queue.pop(); -queue.pop();**注意此时的输出栈的操作** -queue.pop(); -queue.empty(); +执行语句: +queue.push(1); +queue.push(2); +queue.pop(); **注意此时的输出栈的操作** +queue.push(3); +queue.push(4); +queue.pop(); +queue.pop();**注意此时的输出栈的操作** +queue.pop(); +queue.empty(); -![232.用栈实现队列版本2](https://code-thinking.cdn.bcebos.com/gifs/232.%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97%E7%89%88%E6%9C%AC2.gif) +![232.用栈实现队列版本2](https://code-thinking.cdn.bcebos.com/gifs/232.用栈实现队列版本2.gif) 在push数据的时候,只要数据放进输入栈就好,**但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入)**,再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。 @@ -125,8 +125,6 @@ public: - - ## 其他语言版本 Java: diff --git a/problems/0235.二叉搜索树的最近公共祖先.md b/problems/0235.二叉搜索树的最近公共祖先.md index 8ea3cdc5..dffc89e6 100644 --- a/problems/0235.二叉搜索树的最近公共祖先.md +++ b/problems/0235.二叉搜索树的最近公共祖先.md @@ -312,6 +312,7 @@ func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { }else {return findLeft} } ``` + JavaScript版本: 1. 使用递归的方法 ```javascript diff --git a/problems/0236.二叉树的最近公共祖先.md b/problems/0236.二叉树的最近公共祖先.md index 7673a0ab..7b5deb56 100644 --- a/problems/0236.二叉树的最近公共祖先.md +++ b/problems/0236.二叉树的最近公共祖先.md @@ -310,6 +310,7 @@ func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { return nil } ``` + JavaScript版本: ```javascript var lowestCommonAncestor = function(root, p, q) { @@ -337,6 +338,7 @@ var lowestCommonAncestor = function(root, p, q) { ``` + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) diff --git a/problems/0239.滑动窗口最大值.md b/problems/0239.滑动窗口最大值.md index d108335b..ad54a940 100644 --- a/problems/0239.滑动窗口最大值.md +++ b/problems/0239.滑动窗口最大值.md @@ -208,6 +208,7 @@ public: Java: ```Java +//解法一 //自定义数组 class MyQueue { Deque deque = new LinkedList<>(); @@ -260,6 +261,40 @@ class Solution { return res; } } + +//解法二 +//利用双端队列手动实现单调队列 +/** + * 用一个单调队列来存储对应的下标,每当窗口滑动的时候,直接取队列的头部指针对应的值放入结果集即可 + * 单调队列类似 (tail -->) 3 --> 2 --> 1 --> 0 (--> head) (右边为头结点,元素存的是下标) + */ +class Solution { + public int[] maxSlidingWindow(int[] nums, int k) { + ArrayDeque deque = new ArrayDeque<>(); + int n = nums.length; + int[] res = new int[n - k + 1]; + int idx = 0; + for(int i = 0; i < n; i++) { + // 根据题意,i为nums下标,是要在[i - k + 1, k] 中选到最大值,只需要保证两点 + // 1.队列头结点需要在[i - k + 1, k]范围内,不符合则要弹出 + while(!deque.isEmpty() && deque.peek() < i - k + 1){ + deque.poll(); + } + // 2.既然是单调,就要保证每次放进去的数字要比末尾的都大,否则也弹出 + while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) { + deque.pollLast(); + } + + deque.offer(i); + + // 因为单调,当i增长到符合第一个k范围的时候,每滑动一步都将队列头节点放入结果就行了 + if(i >= k - 1){ + res[idx++] = nums[deque.peek()]; + } + } + return res; + } +} ``` Python: diff --git a/problems/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md index fe7a2604..d5a0478e 100644 --- a/problems/0257.二叉树的所有路径.md +++ b/problems/0257.二叉树的所有路径.md @@ -350,6 +350,29 @@ class Solution: ``` Go: + +```go +func binaryTreePaths(root *TreeNode) []string { + res := make([]string, 0) + var travel func(node *TreeNode, s string) + travel = func(node *TreeNode, s string) { + if node.Left == nil && node.Right == nil { + v := s + strconv.Itoa(node.Val) + res = append(res, v) + return + } + s = s + strconv.Itoa(node.Val) + "->" + if node.Left != nil { + travel(node.Left, s) + } + if node.Right != nil { + travel(node.Right, s) + } + } + travel(root, "") + return res +} +``` JavaScript: 1.递归版本 diff --git a/problems/0337.打家劫舍III.md b/problems/0337.打家劫舍III.md index 0a4b752b..3504a574 100644 --- a/problems/0337.打家劫舍III.md +++ b/problems/0337.打家劫舍III.md @@ -287,24 +287,85 @@ class Solution { Python: -> 动态规划 -```python +> 暴力递归 + +```python3 + +# 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 rob(self, root: TreeNode) -> int: - result = self.robTree(root) + if root is None: + return 0 + if root.left is None and root.right is None: + return root.val + # 偷父节点 + val1 = root.val + if root.left: + val1 += self.rob(root.left.left) + self.rob(root.left.right) + if root.right: + val1 += self.rob(root.right.left) + self.rob(root.right.right) + # 不偷父节点 + val2 = self.rob(root.left) + self.rob(root.right) + return max(val1, val2) +``` + +> 记忆化递归 + +```python3 + +# 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: + memory = {} + def rob(self, root: TreeNode) -> int: + if root is None: + return 0 + if root.left is None and root.right is None: + return root.val + if self.memory.get(root) is not None: + return self.memory[root] + # 偷父节点 + val1 = root.val + if root.left: + val1 += self.rob(root.left.left) + self.rob(root.left.right) + if root.right: + val1 += self.rob(root.right.left) + self.rob(root.right.right) + # 不偷父节点 + val2 = self.rob(root.left) + self.rob(root.right) + self.memory[root] = max(val1, val2) + return max(val1, val2) +``` + +> 动态规划 +```python3 +# 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 rob(self, root: TreeNode) -> int: + result = self.rob_tree(root) return max(result[0], result[1]) - #长度为2的数组,0:不偷,1:偷 - def robTree(self, cur): - if not cur: - return (0, 0) #这里返回tuple, 也可以返回list - left = self.robTree(cur.left) - right = self.robTree(cur.right) - #偷cur - val1 = cur.val + left[0] + right[0] - #不偷cur - val2 = max(left[0], left[1]) + max(right[0], right[1]) - return (val2, val1) + def rob_tree(self, node): + if node is None: + return (0, 0) # (偷当前节点金额,不偷当前节点金额) + left = self.rob_tree(node.left) + right = self.rob_tree(node.right) + val1 = node.val + left[1] + right[1] # 偷当前节点,不能偷子节点 + val2 = max(left[0], left[1]) + max(right[0], right[1]) # 不偷当前节点,可偷可不偷子节点 + return (val1, val2) ``` Go: diff --git a/problems/0344.反转字符串.md b/problems/0344.反转字符串.md index 1b86e847..aeb13a30 100644 --- a/problems/0344.反转字符串.md +++ b/problems/0344.反转字符串.md @@ -20,14 +20,13 @@ https://leetcode-cn.com/problems/reverse-string/ 你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。 -示例 1: +示例 1: +输入:["h","e","l","l","o"] +输出:["o","l","l","e","h"] -输入:["h","e","l","l","o"] -输出:["o","l","l","e","h"] -示例 2: - -输入:["H","a","n","n","a","h"] -输出:["h","a","n","n","a","H"] +示例 2: +输入:["H","a","n","n","a","h"] +输出:["h","a","n","n","a","H"] # 思路 @@ -56,7 +55,7 @@ https://leetcode-cn.com/problems/reverse-string/ 接下来再来讲一下如何解决反转字符串的问题。 -大家应该还记得,我们已经讲过了[206.反转链表](https://mp.weixin.qq.com/s/pnvVP-0ZM7epB8y3w_Njwg)。 +大家应该还记得,我们已经讲过了[206.反转链表](https://mp.weixin.qq.com/s/ckEvIVGcNLfrz6OLOMoT0A)。 在反转链表中,使用了双指针的方法。 @@ -64,7 +63,7 @@ https://leetcode-cn.com/problems/reverse-string/ 因为字符串也是一种数组,所以元素在内存中是连续分布,这就决定了反转链表和反转字符串方式上还是有所差异的。 -如果对数组和链表原理不清楚的同学,可以看这两篇,[关于链表,你该了解这些!](https://mp.weixin.qq.com/s/ntlZbEdKgnFQKZkSUAOSpQ),[必须掌握的数组理论知识](https://mp.weixin.qq.com/s/X7R55wSENyY62le0Fiawsg)。 +如果对数组和链表原理不清楚的同学,可以看这两篇,[关于链表,你该了解这些!](https://mp.weixin.qq.com/s/fDGMmLrW7ZHlzkzlf_dZkw),[必须掌握的数组理论知识](https://mp.weixin.qq.com/s/c2KABb-Qgg66HrGf8z-8Og)。 对于字符串,我们定义两个指针(也可以说是索引下表),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。 @@ -119,8 +118,7 @@ s[i] ^= s[j]; 相信大家本着我所讲述的原则来做字符串相关的题目,在选择库函数的角度上会有所原则,也会有所收获。 - -## C++代码 +C++代码如下: ```C++ class Solution { @@ -157,7 +155,7 @@ class Solution { ``` Python: -```python3 +```python class Solution: def reverseString(self, s: List[str]) -> None: """ @@ -168,6 +166,17 @@ class Solution: s[left], s[right] = s[right], s[left] left += 1 right -= 1 + +# 下面的写法更加简洁,但是都是同样的算法 +# class Solution: +# def reverseString(self, s: List[str]) -> None: +# """ +# Do not return anything, modify s in-place instead. +# """ + # 不需要判别是偶数个还是奇数个序列,因为奇数个的时候,中间那个不需要交换就可 +# for i in range(len(s)//2): +# s[i], s[len(s)-1-i] = s[len(s)-1-i], s[i] +# return s ``` Go: diff --git a/problems/0392.判断子序列.md b/problems/0392.判断子序列.md index 9048ac44..d97d2684 100644 --- a/problems/0392.判断子序列.md +++ b/problems/0392.判断子序列.md @@ -140,8 +140,29 @@ public: ## 其他语言版本 -Java: - +Java: +``` +class Solution { + public boolean isSubsequence(String s, String t) { + int length1 = s.length(); int length2 = t.length(); + int[][] dp = new int[length1+1][length2+1]; + for(int i = 1; i <= length1; i++){ + for(int j = 1; j <= length2; j++){ + if(s.charAt(i-1) == t.charAt(j-1)){ + dp[i][j] = dp[i-1][j-1] + 1; + }else{ + dp[i][j] = dp[i][j-1]; + } + } + } + if(dp[length1][length2] == length1){ + return true; + }else{ + return false; + } + } +} +``` Python: ```python diff --git a/problems/0406.根据身高重建队列.md b/problems/0406.根据身高重建队列.md index 9aca5b94..946c41ce 100644 --- a/problems/0406.根据身高重建队列.md +++ b/problems/0406.根据身高重建队列.md @@ -214,13 +214,10 @@ Python: ```python class Solution: def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: - people.sort(key=lambda x: (x[0], -x[1]), reverse=True) + people.sort(key=lambda x: (-x[0], x[1])) que = [] for p in people: - if p[1] > len(que): - que.append(p) - else: - que.insert(p[1], p) + que.insert(p[1], p) return que ``` diff --git a/problems/0450.删除二叉搜索树中的节点.md b/problems/0450.删除二叉搜索树中的节点.md index 3ed44c0a..4695ed50 100644 --- a/problems/0450.删除二叉搜索树中的节点.md +++ b/problems/0450.删除二叉搜索树中的节点.md @@ -67,7 +67,6 @@ if (root == nullptr) return root; 第五种情况有点难以理解,看下面动画: ![450.删除二叉搜索树中的节点](https://tva1.sinaimg.cn/large/008eGmZEly1gnbj3k596mg30dq0aigyz.gif) - 动画中颗二叉搜索树中,删除元素7, 那么删除节点(元素7)的左孩子就是5,删除节点(元素7)的右子树的最左面节点是元素8。 @@ -359,6 +358,51 @@ func deleteNode1(root *TreeNode)*TreeNode{ } ``` +JavaScript版本 + +> 递归 + +```javascript +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @param {number} key + * @return {TreeNode} + */ +var deleteNode = function (root, key) { + if (root === null) + return root; + if (root.val === key) { + if (!root.left) + return root.right; + else if (!root.right) + return root.left; + else { + let cur = root.right; + while (cur.left) { + cur = cur.left; + } + cur.left = root.left; + let temp = root; + root = root.right; + delete root; + return root; + } + } + if (root.val > key) + root.left = deleteNode(root.left, key); + if (root.val < key) + root.right = deleteNode(root.right, key); + return root; +}; +``` diff --git a/problems/0459.重复的子字符串.md b/problems/0459.重复的子字符串.md index deb755bf..368489a5 100644 --- a/problems/0459.重复的子字符串.md +++ b/problems/0459.重复的子字符串.md @@ -17,18 +17,18 @@ https://leetcode-cn.com/problems/repeated-substring-pattern/ 给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。 -示例 1: -输入: "abab" -输出: True -解释: 可由子字符串 "ab" 重复两次构成。 +示例 1: +输入: "abab" +输出: True +解释: 可由子字符串 "ab" 重复两次构成。 -示例 2: -输入: "aba" -输出: False +示例 2: +输入: "aba" +输出: False -示例 3: -输入: "abcabcabcabc" -输出: True +示例 3: +输入: "abcabcabcabc" +输出: True 解释: 可由子字符串 "abc" 重复四次构成。 (或者子字符串 "abcabc" 重复两次构成。) # 思路 @@ -41,13 +41,11 @@ https://leetcode-cn.com/problems/repeated-substring-pattern/ * [帮你把KMP算法学个通透!(求next数组代码篇)](https://www.bilibili.com/video/BV1M5411j7Xx) -如果KMP还不够了解,可以看我的这个视频[帮你把KMP算法学个通透!B站](https://www.bilibili.com/video/BV1PD4y1o7nd/) - -我们在[字符串:都来看看KMP的看家本领!](https://mp.weixin.qq.com/s/Gk9FKZ9_FSWLEkdGrkecyg)里提到了,在一个串中查找是否出现过另一个串,这是KMP的看家本领。 +我们在[字符串:KMP算法精讲](https://mp.weixin.qq.com/s/MoRBHbS4hQXn7LcPdmHmIg)里提到了,在一个串中查找是否出现过另一个串,这是KMP的看家本领。 那么寻找重复子串怎么也涉及到KMP算法了呢? -这里就要说一说next数组了,next 数组记录的就是最长相同前后缀( [字符串:听说你对KMP有这些疑问?](https://mp.weixin.qq.com/s/mqx6IM2AO4kLZwvXdPtEeQ) 这里介绍了什么是前缀,什么是后缀,什么又是最长相同前后缀), 如果 next[len - 1] != -1,则说明字符串有最长相同的前后缀(就是字符串里的前缀子串和后缀子串相同的最长长度)。 +这里就要说一说next数组了,next 数组记录的就是最长相同前后缀( [字符串:KMP算法精讲](https://mp.weixin.qq.com/s/MoRBHbS4hQXn7LcPdmHmIg) 这里介绍了什么是前缀,什么是后缀,什么又是最长相同前后缀), 如果 next[len - 1] != -1,则说明字符串有最长相同的前后缀(就是字符串里的前缀子串和后缀子串相同的最长长度)。 最长相等前后缀的长度为:next[len - 1] + 1。 @@ -62,7 +60,7 @@ https://leetcode-cn.com/problems/repeated-substring-pattern/ 如图: -![459.重复的子字符串_1](https://code-thinking.cdn.bcebos.com/pics/459.%E9%87%8D%E5%A4%8D%E7%9A%84%E5%AD%90%E5%AD%97%E7%AC%A6%E4%B8%B2_1.png) +![459.重复的子字符串_1](https://code-thinking.cdn.bcebos.com/pics/459.重复的子字符串_1.png) next[len - 1] = 7,next[len - 1] + 1 = 8,8就是此时字符串asdfasdfasdf的最长相同前后缀的长度。 @@ -70,7 +68,7 @@ next[len - 1] = 7,next[len - 1] + 1 = 8,8就是此时字符串asdfasdfasdf (len - (next[len - 1] + 1)) 也就是: 12(字符串的长度) - 8(最长公共前后缀的长度) = 4, 4正好可以被 12(字符串的长度) 整除,所以说明有重复的子字符串(asdf)。 -代码如下:(这里使用了前缀表统一减一的实现方式) +C++代码如下:(这里使用了前缀表统一减一的实现方式) ```C++ class Solution { @@ -104,7 +102,7 @@ public: ``` -前缀表(不减一)的代码实现 +前缀表(不减一)的C++代码实现 ```C++ class Solution { @@ -139,12 +137,11 @@ public: # 拓展 -此时我们已经分享了三篇KMP的文章,首先是[字符串:KMP是时候上场了(一文读懂系列)](https://mp.weixin.qq.com/s/70OXnZ4Ez29CKRrUpVJmug)讲解KMP算法的基础理论,给出next数组究竟是如何来了,前缀表又是怎么回事,为什么要选择前缀表。 +在[字符串:KMP算法精讲](https://mp.weixin.qq.com/s/MoRBHbS4hQXn7LcPdmHmIg)中讲解KMP算法的基础理论,给出next数组究竟是如何来了,前缀表又是怎么回事,为什么要选择前缀表。 -然后通过[字符串:都来看看KMP的看家本领!](https://mp.weixin.qq.com/s/Gk9FKZ9_FSWLEkdGrkecyg)讲解一道KMP的经典题目,判断文本串里是否出现过模式串,这里涉及到构造next数组的代码实现,以及使用next数组完成模式串与文本串的匹配过程。 - -后来很多同学反馈说:搞不懂前后缀,什么又是最长相同前后缀(最长公共前后缀我认为这个用词不准确),以及为什么前缀表要统一减一(右移)呢,不减一行不行?针对这些问题,我在[字符串:听说你对KMP有这些疑问?](https://mp.weixin.qq.com/s/mqx6IM2AO4kLZwvXdPtEeQ)中又给出了详细的讲解。 +讲解一道KMP的经典题目,力扣:28. 实现 strStr(),判断文本串里是否出现过模式串,这里涉及到构造next数组的代码实现,以及使用next数组完成模式串与文本串的匹配过程。 +后来很多同学反馈说:搞不懂前后缀,什么又是最长相同前后缀(最长公共前后缀我认为这个用词不准确),以及为什么前缀表要统一减一(右移)呢,不减一行不行?针对这些问题,我在[字符串:KMP算法精讲](https://mp.weixin.qq.com/s/MoRBHbS4hQXn7LcPdmHmIg)给出了详细的讲解。 ## 其他语言版本 @@ -301,4 +298,4 @@ func repeatedSubstringPattern(s string) bool { * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
\ No newline at end of file +
diff --git a/problems/0494.目标和.md b/problems/0494.目标和.md index c917ed5c..a0e07a1f 100644 --- a/problems/0494.目标和.md +++ b/problems/0494.目标和.md @@ -158,7 +158,7 @@ dp[j] 表示:填满j(包括j)这么大容积的包,有dp[i]种方法 那么只需要搞到一个2(nums[i]),有dp[3]方法可以凑齐容量为3的背包,相应的就有多少种方法可以凑齐容量为5的背包。 -那么需要把 这些方法累加起来就可以了,dp[i] += dp[j - nums[i]] +那么需要把 这些方法累加起来就可以了,dp[j] += dp[j - nums[i]] 所以求组合类问题的公式,都是类似这种: diff --git a/problems/0501.二叉搜索树中的众数.md b/problems/0501.二叉搜索树中的众数.md index 74c53458..f4b239c3 100644 --- a/problems/0501.二叉搜索树中的众数.md +++ b/problems/0501.二叉搜索树中的众数.md @@ -522,6 +522,7 @@ func traversal(root *TreeNode,result *[]int,pre *TreeNode){//遍历统计 } } ``` + JavaScript版本: 使用额外空间map的方法: ```javascript diff --git a/problems/0530.二叉搜索树的最小绝对差.md b/problems/0530.二叉搜索树的最小绝对差.md index 0bbc4908..8fa756bc 100644 --- a/problems/0530.二叉搜索树的最小绝对差.md +++ b/problems/0530.二叉搜索树的最小绝对差.md @@ -256,6 +256,39 @@ func findMIn(root *TreeNode,res *[]int){ } ``` +JavaScript版本 + +```javascript +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var getMinimumDifference = function (root) { + let arr = []; + const buildArr = (root) => { + if (root) { + buildArr(root.left); + arr.push(root.val); + buildArr(root.right); + } + } + buildArr(root); + let diff = arr[arr.length - 1]; + for (let i = 1; i < arr.length; ++i) { + if (diff > arr[i] - arr[i - 1]) + diff = arr[i] - arr[i - 1]; + } + return diff; +}; +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) diff --git a/problems/0538.把二叉搜索树转换为累加树.md b/problems/0538.把二叉搜索树转换为累加树.md index 765e78d3..06a53ce7 100644 --- a/problems/0538.把二叉搜索树转换为累加树.md +++ b/problems/0538.把二叉搜索树转换为累加树.md @@ -219,6 +219,26 @@ class Solution: Go: +> 弄一个sum暂存其和值 + +```go + //右中左 +func bstToGst(root *TreeNode) *TreeNode { + var sum int + RightMLeft(root,&sum) + return root +} +func RightMLeft(root *TreeNode,sum *int) *TreeNode { + if root==nil{return nil}//终止条件,遇到空节点就返回 + RightMLeft(root.Right,sum)//先遍历右边 + temp:=*sum//暂存总和值 + *sum+=root.Val//将总和值变更 + root.Val+=temp//更新节点值 + RightMLeft(root.Left,sum)//遍历左节点 + return root +} +``` + ----------------------- diff --git a/problems/0541.反转字符串II.md b/problems/0541.反转字符串II.md index 00581fc0..47e85161 100644 --- a/problems/0541.反转字符串II.md +++ b/problems/0541.反转字符串II.md @@ -22,8 +22,8 @@ https://leetcode-cn.com/problems/reverse-string-ii/ 示例: -输入: s = "abcdefg", k = 2 -输出: "bacdfeg" +输入: s = "abcdefg", k = 2 +输出: "bacdfeg" # 思路 @@ -38,7 +38,7 @@ https://leetcode-cn.com/problems/reverse-string-ii/ **所以当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。** 性能如下: - + 那么这里具体反转的逻辑我们要不要使用库函数呢,其实用不用都可以,使用reverse来实现反转也没毛病,毕竟不是解题关键部分。 @@ -65,7 +65,7 @@ public: }; ``` -那么我们也可以实现自己的reverse函数,其实和题目[344. 反转字符串](https://mp.weixin.qq.com/s/X02S61WCYiCEhaik6VUpFA)道理是一样的。 +那么我们也可以实现自己的reverse函数,其实和题目[344. 反转字符串](https://mp.weixin.qq.com/s/_rNm66OJVl92gBDIbGpA3w)道理是一样的。 下面我实现的reverse函数区间是左闭右闭区间,代码如下: @@ -103,6 +103,7 @@ public: Java: ```Java +//解法一 class Solution { public String reverseStr(String s, int k) { StringBuffer res = new StringBuffer(); @@ -128,6 +129,28 @@ class Solution { return res.toString(); } } + +//解法二(似乎更容易理解点) +//题目的意思其实概括为 每隔2k个反转前k个,尾数不够k个时候全部反转 +class Solution { + public String reverseStr(String s, int k) { + char[] ch = s.toCharArray(); + for(int i = 0; i < ch.length; i += 2 * k){ + int start = i; + //这里是判断尾数够不够k个来取决end指针的位置 + int end = Math.min(ch.length - 1, start + k - 1); + //用异或运算反转 + while(start < end){ + ch[start] ^= ch[end]; + ch[end] ^= ch[start]; + ch[start] ^= ch[end]; + start++; + end--; + } + } + return new String(ch); + } +} ``` Python: diff --git a/problems/0617.合并二叉树.md b/problems/0617.合并二叉树.md index be6bb425..f325df64 100644 --- a/problems/0617.合并二叉树.md +++ b/problems/0617.合并二叉树.md @@ -368,8 +368,52 @@ func mergeTrees(t1 *TreeNode, t2 *TreeNode) *TreeNode { Right: mergeTrees(t1.Right,t2.Right)} return root } + +// 前序遍历简洁版 +func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode { + if root1 == nil { + return root2 + } + if root2 == nil { + return root1 + } + root1.Val += root2.Val + root1.Left = mergeTrees(root1.Left, root2.Left) + root1.Right = mergeTrees(root1.Right, root2.Right) + return root1 +} ``` +JavaScript: + +```javascript +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ +var mergeTrees = function (root1, root2) { + const preOrder = (root1, root2) => { + if (!root1) + return root2 + if (!root2) + return root1; + root1.val += root2.val; + root1.left = preOrder(root1.left, root2.left); + root1.right = preOrder(root1.right, root2.right); + return root1; + } + return preOrder(root1, root2); +}; +``` ----------------------- diff --git a/problems/0654.最大二叉树.md b/problems/0654.最大二叉树.md index f0d3e594..4e1e7a72 100644 --- a/problems/0654.最大二叉树.md +++ b/problems/0654.最大二叉树.md @@ -311,6 +311,42 @@ func findMax(nums []int) (index int){ } ``` +JavaScript版本 + +```javascript +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {number[]} nums + * @return {TreeNode} + */ +var constructMaximumBinaryTree = function (nums) { + const BuildTree = (arr, left, right) => { + if (left > right) + return null; + let maxValue = -1; + let maxIndex = -1; + for (let i = left; i <= right; ++i) { + if (arr[i] > maxValue) { + maxValue = arr[i]; + maxIndex = i; + } + } + let root = new TreeNode(maxValue); + root.left = BuildTree(arr, left, maxIndex - 1); + root.right = BuildTree(arr, maxIndex + 1, right); + return root; + } + let root = BuildTree(nums, 0, nums.length - 1); + return root; +}; +``` diff --git a/problems/0669.修剪二叉搜索树.md b/problems/0669.修剪二叉搜索树.md index 06d99b9d..2a88f195 100644 --- a/problems/0669.修剪二叉搜索树.md +++ b/problems/0669.修剪二叉搜索树.md @@ -286,7 +286,33 @@ class Solution: return root ``` Go: +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func trimBST(root *TreeNode, low int, high int) *TreeNode { + if root==nil{ + return nil + } + if root.Valhigh{//如果该节点的值大于最大值,则该节点更换为该节点的左节点值,继续遍历 + left:=trimBST(root.Left,low,high) + return left + } + root.Left=trimBST(root.Left,low,high) + root.Right=trimBST(root.Right,low,high) + return root +} +``` diff --git a/problems/0700.二叉搜索树中的搜索.md b/problems/0700.二叉搜索树中的搜索.md index 16b21f26..d6899ac5 100644 --- a/problems/0700.二叉搜索树中的搜索.md +++ b/problems/0700.二叉搜索树中的搜索.md @@ -290,7 +290,64 @@ func searchBST(root *TreeNode, val int) *TreeNode { } ``` +JavaScript版本 +> 递归 + +```javascript +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ +var searchBST = function (root, val) { + if (!root || root.val === val) { + return root; + } + if (root.val > val) + return searchBST(root.left, val); + if (root.val < val) + return searchBST(root.right, val); + return null; +}; +``` + +> 迭代 + +```javascript +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ +var searchBST = function (root, val) { + while (root !== null) { + if (root.val > val) + root = root.left; + else if (root.val < val) + root = root.right; + else + return root; + } + return root; +}; +``` diff --git a/problems/0701.二叉搜索树中的插入操作.md b/problems/0701.二叉搜索树中的插入操作.md index 6a8ba7fc..794e0ae2 100644 --- a/problems/0701.二叉搜索树中的插入操作.md +++ b/problems/0701.二叉搜索树中的插入操作.md @@ -286,8 +286,118 @@ func insertIntoBST(root *TreeNode, val int) *TreeNode { } ``` +JavaScript版本 +> 有返回值的递归写法 +```javascript +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ +var insertIntoBST = function (root, val) { + const setInOrder = (root, val) => { + if (root === null) { + let node = new TreeNode(val); + return node; + } + if (root.val > val) + root.left = setInOrder(root.left, val); + else if (root.val < val) + root.right = setInOrder(root.right, val); + return root; + } + return setInOrder(root, val); +}; +``` + +> 无返回值的递归 + +```javascript +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ +var insertIntoBST = function (root, val) { + let parent = new TreeNode(0); + const preOrder = (cur, val) => { + if (cur === null) { + let node = new TreeNode(val); + if (parent.val > val) + parent.left = node; + else + parent.right = node; + return; + } + parent = cur; + if (cur.val > val) + preOrder(cur.left, val); + if (cur.val < val) + preOrder(cur.right, val); + } + if (root === null) + root = new TreeNode(val); + preOrder(root, val); + return root; +}; +``` + +> 迭代 + +```javascript +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ +var insertIntoBST = function (root, val) { + if (root === null) { + root = new TreeNode(val); + } else { + let parent = new TreeNode(0); + let cur = root; + while (cur) { + parent = cur; + if (cur.val > val) + cur = cur.left; + else + cur = cur.right; + } + let node = new TreeNode(val); + if (parent.val > val) + parent.left = node; + else + parent.right = node; + } + return root; +}; +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) diff --git a/problems/0714.买卖股票的最佳时机含手续费.md b/problems/0714.买卖股票的最佳时机含手续费.md index abd20625..2e8f9208 100644 --- a/problems/0714.买卖股票的最佳时机含手续费.md +++ b/problems/0714.买卖股票的最佳时机含手续费.md @@ -217,7 +217,29 @@ class Solution: # 贪心思路 Go: +Javascript: +```Javascript +// 贪心思路 +var maxProfit = function(prices, fee) { + let result = 0 + let minPrice = prices[0] + for(let i = 1; i < prices.length; i++) { + if(prices[i] < minPrice) { + minPrice = prices[i] + } + if(prices[i] >= minPrice && prices[i] <= minPrice + fee) { + continue + } + if(prices[i] > minPrice + fee) { + result += prices[i] - minPrice - fee + // 买入和卖出只需要支付一次手续费 + minPrice = prices[i] -fee + } + } + return result +}; +``` ----------------------- diff --git a/problems/0738.单调递增的数字.md b/problems/0738.单调递增的数字.md index 5bddb234..75c7f250 100644 --- a/problems/0738.单调递增的数字.md +++ b/problems/0738.单调递增的数字.md @@ -147,23 +147,43 @@ class Solution { Python: -```python +```python3 class Solution: def monotoneIncreasingDigits(self, n: int) -> int: - strNum = list(str(n)) - flag = len(strNum) - for i in range(len(strNum) - 1, 0, -1): - if int(strNum[i]) < int(strNum[i - 1]): - strNum[i - 1] = str(int(strNum[i - 1]) - 1) - flag = i - for i in range(flag, len(strNum)): - strNum[i] = '9' - return int("".join(strNum)) + 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)) ``` Go: +Javascript: +```Javascript +var monotoneIncreasingDigits = function(n) { + n = n.toString() + n = n.split('').map(item => { + return +item + }) + let flag = Infinity + for(let i = n.length - 1; i > 0; i--) { + if(n [i - 1] > n[i]) { + flag = i + n[i - 1] = n[i - 1] - 1 + n[i] = 9 + } + } + for(let i = flag; i < n.length; i++) { + n[i] = 9 + } + + n = n.join('') + return +n +}; +``` ----------------------- diff --git a/problems/0739.每日温度.md b/problems/0739.每日温度.md index eeea6ead..e72fd6a4 100644 --- a/problems/0739.每日温度.md +++ b/problems/0739.每日温度.md @@ -217,47 +217,46 @@ Go: > 暴力法 ```go -func dailyTemperatures(temperatures []int) []int { - length:=len(temperatures) - res:=make([]int,length) - for i:=0;i t[i] { + res = append(res, j-i) + break + } } - for j=temperatures[j]{//大于等于 - j++ - } - if j 单调栈法 ```go -func dailyTemperatures(temperatures []int) []int { - length:=len(temperatures) - res:=make([]int,length) - stack:=[]int{} - for i:=0;i0&&temperatures[i]>temperatures[stack[len(stack)-1]]{ - res[stack[len(stack)-1]]=i-stack[len(stack)-1]//存放结果集 - stack=stack[:len(stack)-1]//删除stack[len(stack)-1]的元素 - } - //如果栈顶元素大于等于新来的元素,则加入到栈中。当栈内元素个数为0时,直接入栈 - if len(stack)==0||temperatures[i]<=temperatures[stack[len(stack)-1]]{ - stack = append(stack, i) +// 单调递减栈 +func dailyTemperatures(num []int) []int { + ans := make([]int, len(num)) + stack := []int{} + for i, v := range num { + // 栈不空,且当前遍历元素 v 破坏了栈的单调性 + for len(stack) != 0 && v > num[stack[len(stack)-1]] { + // pop + top := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + ans[top] = i - top } + stack = append(stack, i) } - return res + return ans } ``` diff --git a/problems/0763.划分字母区间.md b/problems/0763.划分字母区间.md index 1e2fcc03..b36e00b7 100644 --- a/problems/0763.划分字母区间.md +++ b/problems/0763.划分字母区间.md @@ -128,6 +128,33 @@ class Solution: Go: +```go + +func partitionLabels(s string) []int { + var res []int; + var marks [26]int; + size, left, right := len(s), 0, 0; + for i := 0; i < size; i++ { + marks[s[i] - 'a'] = i; + } + for i := 0; i < size; i++ { + right = max(right, marks[s[i] - 'a']); + if i == right { + res = append(res, right - left + 1); + left = i + 1; + } + } + return res; +} + +func max(a, b int) int { + if a < b { + a = b; + } + return a; +} +``` + Javascript: ```Javascript var partitionLabels = function(s) { diff --git a/problems/0968.监控二叉树.md b/problems/0968.监控二叉树.md index 8f1a3fdb..a0eb5883 100644 --- a/problems/0968.监控二叉树.md +++ b/problems/0968.监控二叉树.md @@ -369,7 +369,42 @@ class Solution: ``` Go: +Javascript: +```Javascript +var minCameraCover = function(root) { + let result = 0 + function traversal(cur) { + if(cur === null) { + return 2 + } + let left = traversal(cur.left) + let right = traversal(cur.right) + + if(left === 2 && right === 2) { + return 0 + } + + if(left === 0 || right === 0) { + result++ + return 1 + } + + if(left === 1 || right === 1) { + return 2 + } + + return -1 + } + + if(traversal(root) === 0) { + result++ + } + + return result + +}; +``` ----------------------- diff --git a/problems/1035.不相交的线.md b/problems/1035.不相交的线.md index be159543..c7481306 100644 --- a/problems/1035.不相交的线.md +++ b/problems/1035.不相交的线.md @@ -26,7 +26,8 @@ 拿示例一A = [1,4,2], B = [1,2,4]为例,相交情况如图: -![1035.不相交的线](https://img-blog.csdnimg.cn/20210321164517460.png) +![](https://gitee.com/programmercarl/pics/raw/master/pic1/20210527113415.png) + 其实也就是说A和B的最长公共子序列是[1,4],长度为2。 这个公共子序列指的是相对顺序不变(即数字4在字符串A中数字1的后面,那么数字4也应该在字符串B数字1的后面) diff --git a/problems/1047.删除字符串中的所有相邻重复项.md b/problems/1047.删除字符串中的所有相邻重复项.md index 305a287d..760a0946 100644 --- a/problems/1047.删除字符串中的所有相邻重复项.md +++ b/problems/1047.删除字符串中的所有相邻重复项.md @@ -127,7 +127,9 @@ Java: ```Java class Solution { public String removeDuplicates(String S) { - Deque deque = new LinkedList<>(); + //ArrayDeque会比LinkedList在除了删除元素这一点外会快一点 + //参考:https://stackoverflow.com/questions/6163166/why-is-arraydeque-better-than-linkedlist + ArrayDeque deque = new ArrayDeque<>(); char ch; for (int i = 0; i < S.length(); i++) { ch = S.charAt(i); @@ -171,6 +173,29 @@ class Solution { } ``` +拓展:双指针 +```java +class Solution { + public String removeDuplicates(String s) { + char[] ch = s.toCharArray(); + int fast = 0; + int slow = 0; + while(fast < s.length()){ + // 直接用fast指针覆盖slow指针的值 + ch[slow] = ch[fast]; + // 遇到前后相同值的,就跳过,即slow指针后退一步,下次循环就可以直接被覆盖掉了 + if(slow > 0 && ch[slow] == ch[slow - 1]){ + slow--; + }else{ + slow++; + } + fast++; + } + return new String(ch,0,slow); + } +} +``` + Python: ```python3 class Solution: diff --git a/problems/二叉树的迭代遍历.md b/problems/二叉树的迭代遍历.md index 8706dc47..30b921ff 100644 --- a/problems/二叉树的迭代遍历.md +++ b/problems/二叉树的迭代遍历.md @@ -155,9 +155,82 @@ public: ## 其他语言版本 - Java: +```java +// 前序遍历顺序:中-左-右,入栈顺序:中-右-左 +class Solution { + public List preorderTraversal(TreeNode root) { + List result = new ArrayList<>(); + if (root == null){ + return result; + } + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()){ + TreeNode node = stack.pop(); + result.add(node.val); + if (node.right != null){ + stack.push(node.right); + } + if (node.left != null){ + stack.push(node.left); + } + } + return result; + } +} + +// 中序遍历顺序: 左-中-右 入栈顺序: 左-右 +class Solution { + public List inorderTraversal(TreeNode root) { + List result = new ArrayList<>(); + if (root == null){ + return result; + } + Stack stack = new Stack<>(); + TreeNode cur = root; + while (cur != null || !stack.isEmpty()){ + if (cur != null){ + stack.push(cur); + cur = cur.left; + }else{ + cur = stack.pop(); + result.add(cur.val); + cur = cur.right; + } + } + return result; + } +} + +// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果 +class Solution { + public List postorderTraversal(TreeNode root) { + List result = new ArrayList<>(); + if (root == null){ + return result; + } + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()){ + TreeNode node = stack.pop(); + result.add(node.val); + if (node.left != null){ + stack.push(node.left); + } + if (node.right != null){ + stack.push(node.right); + } + } + Collections.reverse(result); + return result; + } +} +``` + + + Python: ```python3 diff --git a/problems/剑指Offer05.替换空格.md b/problems/剑指Offer05.替换空格.md index a4c0149f..f68d8e22 100644 --- a/problems/剑指Offer05.替换空格.md +++ b/problems/剑指Offer05.替换空格.md @@ -13,9 +13,9 @@ https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof/ 请实现一个函数,把字符串 s 中的每个空格替换成"%20"。 -示例 1: -输入:s = "We are happy." -输出:"We%20are%20happy." +示例 1: +输入:s = "We are happy." +输出:"We%20are%20happy." # 思路 @@ -42,9 +42,9 @@ i指向新长度的末尾,j指向旧长度的末尾。 时间复杂度,空间复杂度均超过100%的用户。 - + -## C++代码 +C++代码如下: ```C++ class Solution { @@ -76,17 +76,17 @@ public: }; ``` -时间复杂度:O(n) -空间复杂度:O(1) +* 时间复杂度:O(n) +* 空间复杂度:O(1) 此时算上本题,我们已经做了七道双指针相关的题目了分别是: -* [27.移除元素](https://mp.weixin.qq.com/s/wj0T-Xs88_FHJFwayElQlA) -* [15.三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A) -* [18.四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g) -* [206.翻转链表](https://mp.weixin.qq.com/s/pnvVP-0ZM7epB8y3w_Njwg) -* [142.环形链表II](https://mp.weixin.qq.com/s/_QVP3IkRZWx9zIpQRgajzA) -* [344.反转字符串](https://mp.weixin.qq.com/s/X02S61WCYiCEhaik6VUpFA) +* [27.移除元素](https://mp.weixin.qq.com/s/RMkulE4NIb6XsSX83ra-Ww) +* [15.三数之和](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg) +* [18.四数之和](https://mp.weixin.qq.com/s/SBU3THi1Kv6Sar7htqCB2Q) +* [206.翻转链表](https://mp.weixin.qq.com/s/ckEvIVGcNLfrz6OLOMoT0A) +* [142.环形链表II](https://mp.weixin.qq.com/s/gt_VH3hQTqNxyWcl1ECSbQ) +* [344.反转字符串](https://mp.weixin.qq.com/s/_rNm66OJVl92gBDIbGpA3w) # 拓展 @@ -121,10 +121,6 @@ for (int i = 0; i < a.size(); i++) { 所以想处理字符串,我们还是会定义一个string类型。 - - - - ## 其他语言版本 @@ -150,8 +146,6 @@ public static String replaceSpace(StringBuffer str) { } ``` -Python: - Go: ```go diff --git a/problems/剑指Offer58-II.左旋转字符串.md b/problems/剑指Offer58-II.左旋转字符串.md index 39c8382c..70c6f015 100644 --- a/problems/剑指Offer58-II.左旋转字符串.md +++ b/problems/剑指Offer58-II.左旋转字符串.md @@ -16,16 +16,16 @@ https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/ 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。 -示例 1: -输入: s = "abcdefg", k = 2 -输出: "cdefgab" +示例 1: +输入: s = "abcdefg", k = 2 +输出: "cdefgab" -示例 2: -输入: s = "lrloseumgh", k = 6 -输出: "umghlrlose" +示例 2: +输入: s = "lrloseumgh", k = 6 +输出: "umghlrlose" -限制: -1 <= k < s.length <= 10000 +限制: +1 <= k < s.length <= 10000 # 思路 @@ -34,7 +34,7 @@ https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/ 不能使用额外空间的话,模拟在本串操作要实现左旋转字符串的功能还是有点困难的。 -那么我们可以想一下上一题目[字符串:花式反转还不够!](https://mp.weixin.qq.com/s/X3qpi2v5RSp08mO-W5Vicw)中讲过,使用整体反转+局部反转就可以实现,反转单词顺序的目的。 +那么我们可以想一下上一题目[字符串:花式反转还不够!](https://mp.weixin.qq.com/s/4j6vPFHkFAXnQhmSkq2X9g)中讲过,使用整体反转+局部反转就可以实现,反转单词顺序的目的。 这道题目也非常类似,依然可以通过局部反转+整体反转 达到左旋转的目的。 @@ -50,13 +50,13 @@ https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/ 如图: - + 最终得到左旋2个单元的字符串:cdefgab 思路明确之后,那么代码实现就很简单了 -# C++代码 +C++代码如下: ```C++ class Solution { @@ -73,15 +73,16 @@ public: # 总结 + 此时我们已经反转好多次字符串了,来一起回顾一下吧。 -在这篇文章[字符串:这道题目,使用库函数一行代码搞定](https://mp.weixin.qq.com/s/X02S61WCYiCEhaik6VUpFA),第一次讲到反转一个字符串应该怎么做,使用了双指针法。 +在这篇文章[344.反转字符串](https://mp.weixin.qq.com/s/_rNm66OJVl92gBDIbGpA3w),第一次讲到反转一个字符串应该怎么做,使用了双指针法。 -然后发现[字符串:简单的反转还不够!](https://mp.weixin.qq.com/s/XGSk1GyPWhfqj2g7Cb1Vgw),这里开始给反转加上了一些条件,当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。 +然后发现[541. 反转字符串II](https://mp.weixin.qq.com/s/pzXt6PQ029y7bJ9YZB2mVQ),这里开始给反转加上了一些条件,当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。 -后来在[字符串:花式反转还不够!](https://mp.weixin.qq.com/s/X3qpi2v5RSp08mO-W5Vicw)中,要对一句话里的单词顺序进行反转,发现先整体反转再局部反转 是一个很妙的思路。 +后来在[151.翻转字符串里的单词](https://mp.weixin.qq.com/s/4j6vPFHkFAXnQhmSkq2X9g)中,要对一句话里的单词顺序进行反转,发现先整体反转再局部反转 是一个很妙的思路。 -最后再讲到本地,本题则是先局部反转再 整体反转,与[字符串:花式反转还不够!](https://mp.weixin.qq.com/s/X3qpi2v5RSp08mO-W5Vicw)类似,但是也是一种新的思路。 +最后再讲到本题,本题则是先局部反转再 整体反转,与[151.翻转字符串里的单词](https://mp.weixin.qq.com/s/4j6vPFHkFAXnQhmSkq2X9g)类似,但是也是一种新的思路。 好了,反转字符串一共就介绍到这里,相信大家此时对反转字符串的常见操作已经很了解了。 @@ -93,7 +94,6 @@ public: **如果想让这套题目有意义,就不要申请额外空间。** - ## 其他语言版本 Java: @@ -117,7 +117,6 @@ class Solution { } } ``` -Python: Go: @@ -151,4 +150,4 @@ func reverse(b []byte, left, right int){ * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) * 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) -
\ No newline at end of file +
diff --git a/problems/双指针总结.md b/problems/双指针总结.md index 13f2f174..b03d3ff2 100644 --- a/problems/双指针总结.md +++ b/problems/双指针总结.md @@ -12,7 +12,7 @@ # 数组篇 -在[数组:就移除个元素很难么?](https://mp.weixin.qq.com/s/wj0T-Xs88_FHJFwayElQlA)中,原地移除数组上的元素,我们说到了数组上的元素,不能真正的删除,只能覆盖。 +在[数组:就移除个元素很难么?](https://mp.weixin.qq.com/s/RMkulE4NIb6XsSX83ra-Ww)中,原地移除数组上的元素,我们说到了数组上的元素,不能真正的删除,只能覆盖。 一些同学可能会写出如下代码(伪代码): @@ -30,11 +30,11 @@ for (int i = 0; i < array.size(); i++) { # 字符串篇 -在[字符串:这道题目,使用库函数一行代码搞定](https://mp.weixin.qq.com/s/X02S61WCYiCEhaik6VUpFA)中讲解了反转字符串,注意这里强调要原地反转,要不然就失去了题目的意义。 +在[字符串:这道题目,使用库函数一行代码搞定](https://mp.weixin.qq.com/s/_rNm66OJVl92gBDIbGpA3w)中讲解了反转字符串,注意这里强调要原地反转,要不然就失去了题目的意义。 使用双指针法,**定义两个指针(也可以说是索引下表),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。**,时间复杂度是O(n)。 -在[替换空格](https://mp.weixin.qq.com/s/t0A9C44zgM-RysAQV3GZpg) 中介绍使用双指针填充字符串的方法,如果想把这道题目做到极致,就不要只用额外的辅助空间了! +在[替换空格](https://mp.weixin.qq.com/s/69HNjR4apcRSAo_KyknPjA) 中介绍使用双指针填充字符串的方法,如果想把这道题目做到极致,就不要只用额外的辅助空间了! 思路就是**首先扩充数组到每个空格替换成"%20"之后的大小。然后双指针从后向前替换空格。** @@ -44,7 +44,7 @@ for (int i = 0; i < array.size(); i++) { **其实很多数组(字符串)填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。** -那么在[字符串:花式反转还不够!](https://mp.weixin.qq.com/s/X3qpi2v5RSp08mO-W5Vicw)中,我们使用双指针法,用O(n)的时间复杂度完成字符串删除类的操作,因为题目要产出冗余空格。 +那么在[字符串:花式反转还不够!](https://mp.weixin.qq.com/s/4j6vPFHkFAXnQhmSkq2X9g)中,我们使用双指针法,用O(n)的时间复杂度完成字符串删除类的操作,因为题目要产出冗余空格。 **在删除冗余空格的过程中,如果不注意代码效率,很容易写成了O(n^2)的时间复杂度。其实使用双指针法O(n)就可以搞定。** @@ -54,19 +54,19 @@ for (int i = 0; i < array.size(); i++) { 翻转链表是现场面试,白纸写代码的好题,考察了候选者对链表以及指针的熟悉程度,而且代码也不长,适合在白纸上写。 -在[链表:听说过两天反转链表又写不出来了?](https://mp.weixin.qq.com/s/pnvVP-0ZM7epB8y3w_Njwg)中,讲如何使用双指针法来翻转链表,**只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表。** +在[链表:听说过两天反转链表又写不出来了?](https://mp.weixin.qq.com/s/ckEvIVGcNLfrz6OLOMoT0A)中,讲如何使用双指针法来翻转链表,**只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表。** 思路还是很简单的,代码也不长,但是想在白纸上一次性写出bugfree的代码,并不是容易的事情。 -在链表中求环,应该是双指针在链表里最经典的应用,在[链表:环找到了,那入口呢?](https://mp.weixin.qq.com/s/_QVP3IkRZWx9zIpQRgajzA)中讲解了如何通过双指针判断是否有环,而且还要找到环的入口。 +在链表中求环,应该是双指针在链表里最经典的应用,在[链表:环找到了,那入口呢?](https://mp.weixin.qq.com/s/gt_VH3hQTqNxyWcl1ECSbQ)中讲解了如何通过双指针判断是否有环,而且还要找到环的入口。 **使用快慢指针(双指针法),分别定义 fast 和 slow指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。** -那么找到环的入口,其实需要点简单的数学推理,我在文章中把找环的入口清清楚楚的推理的一遍,如果对找环入口不够清楚的同学建议自己看一看[链表:环找到了,那入口呢?](https://mp.weixin.qq.com/s/_QVP3IkRZWx9zIpQRgajzA)。 +那么找到环的入口,其实需要点简单的数学推理,我在文章中把找环的入口清清楚楚的推理的一遍,如果对找环入口不够清楚的同学建议自己看一看[链表:环找到了,那入口呢?](https://mp.weixin.qq.com/s/gt_VH3hQTqNxyWcl1ECSbQ)。 # N数之和篇 -在[哈希表:解决了两数之和,那么能解决三数之和么?](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)中,讲到使用哈希法可以解决1.两数之和的问题 +在[哈希表:解决了两数之和,那么能解决三数之和么?](https://mp.weixin.qq.com/s/QfTNEByq1YlNSXRKEumwHg)中,讲到使用哈希法可以解决1.两数之和的问题 其实使用双指针也可以解决1.两数之和的问题,只不过1.两数之和求的是两个元素的下标,没法用双指针,如果改成求具体两个元素的数值就可以了,大家可以尝试用双指针做一个leetcode上两数之和的题目,就可以体会到我说的意思了。 @@ -82,7 +82,7 @@ for (int i = 0; i < array.size(); i++) { 只用双指针法时间复杂度为O(n^2),但比哈希法的O(n^2)效率高得多,哈希法在使用两层for循环的时候,能做的剪枝操作很有限。 -在[双指针法:一样的道理,能解决四数之和](https://mp.weixin.qq.com/s/nQrcco8AZJV1pAOVjeIU_g)中,讲到了四数之和,其实思路是一样的,**在三数之和的基础上再套一层for循环,依然是使用双指针法。** +在[双指针法:一样的道理,能解决四数之和](https://mp.weixin.qq.com/s/SBU3THi1Kv6Sar7htqCB2Q)中,讲到了四数之和,其实思路是一样的,**在三数之和的基础上再套一层for循环,依然是使用双指针法。** 对于三数之和使用双指针法就是将原本暴力O(n^3)的解法,降为O(n^2)的解法,四数之和的双指针解法就是将原本暴力O(n^4)的解法,降为O(n^3)的解法。 @@ -94,18 +94,6 @@ for (int i = 0; i < array.size(); i++) { 本文中一共介绍了leetcode上九道使用双指针解决问题的经典题目,除了链表一些题目一定要使用双指针,其他题目都是使用双指针来提高效率,一般是将O(n^2)的时间复杂度,降为O(n)。 建议大家可以把文中涉及到的题目在好好做一做,琢磨琢磨,基本对双指针法就不在话下了。 -## 其他语言版本 - - -Java: - - -Python: - - -Go: - - ----------------------- diff --git a/problems/字符串总结.md b/problems/字符串总结.md index 21fdc9bc..71be6422 100644 --- a/problems/字符串总结.md +++ b/problems/字符串总结.md @@ -43,9 +43,10 @@ for (int i = 0; i < a.size(); i++) { 所以想处理字符串,我们还是会定义一个string类型。 + # 要不要使用库函数 -在文章[字符串:这道题目,使用库函数一行代码搞定](https://mp.weixin.qq.com/s/X02S61WCYiCEhaik6VUpFA)中强调了**打基础的时候,不要太迷恋于库函数。** +在文章[344.反转字符串](https://mp.weixin.qq.com/s/_rNm66OJVl92gBDIbGpA3w)中强调了**打基础的时候,不要太迷恋于库函数。** 甚至一些同学习惯于调用substr,split,reverse之类的库函数,却不知道其实现原理,也不知道其时间复杂度,这样实现出来的代码,如果在面试现场,面试官问:“分析其时间复杂度”的话,一定会一脸懵逼! @@ -56,15 +57,15 @@ for (int i = 0; i < a.size(); i++) { # 双指针法 -在[字符串:这道题目,使用库函数一行代码搞定](https://mp.weixin.qq.com/s/X02S61WCYiCEhaik6VUpFA) ,我们使用双指针法实现了反转字符串的操作,**双指针法在数组,链表和字符串中很常用。** +在[344.反转字符串](https://mp.weixin.qq.com/s/_rNm66OJVl92gBDIbGpA3w) ,我们使用双指针法实现了反转字符串的操作,**双指针法在数组,链表和字符串中很常用。** -接着在[字符串:替换空格](https://mp.weixin.qq.com/s/t0A9C44zgM-RysAQV3GZpg),同样还是使用双指针法在时间复杂度O(n)的情况下完成替换空格。 +接着在[字符串:替换空格](https://mp.weixin.qq.com/s/69HNjR4apcRSAo_KyknPjA),同样还是使用双指针法在时间复杂度O(n)的情况下完成替换空格。 **其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。** -那么针对数组删除操作的问题,其实在[数组:就移除个元素很难么?](https://mp.weixin.qq.com/s/wj0T-Xs88_FHJFwayElQlA)中就已经提到了使用双指针法进行移除操作。 +那么针对数组删除操作的问题,其实在[27. 移除元素](https://mp.weixin.qq.com/s/RMkulE4NIb6XsSX83ra-Ww)中就已经提到了使用双指针法进行移除操作。 -同样的道理在[字符串:花式反转还不够!](https://mp.weixin.qq.com/s/X3qpi2v5RSp08mO-W5Vicw)中我们使用O(n)的时间复杂度,完成了删除冗余空格。 +同样的道理在[151.翻转字符串里的单词](https://mp.weixin.qq.com/s/4j6vPFHkFAXnQhmSkq2X9g)中我们使用O(n)的时间复杂度,完成了删除冗余空格。 一些同学会使用for循环里调用库函数erase来移除元素,这其实是O(n^2)的操作,因为erase就是O(n)的操作,所以这也是典型的不知道库函数的时间复杂度,上来就用的案例了。 @@ -72,7 +73,7 @@ for (int i = 0; i < a.size(); i++) { 在反转上还可以在加一些玩法,其实考察的是对代码的掌控能力。 -[字符串:简单的反转还不够!](https://mp.weixin.qq.com/s/XGSk1GyPWhfqj2g7Cb1Vgw)中,一些同学可能为了处理逻辑:每隔2k个字符的前k的字符,写了一堆逻辑代码或者再搞一个计数器,来统计2k,再统计前k个字符。 +[541. 反转字符串II](https://mp.weixin.qq.com/s/pzXt6PQ029y7bJ9YZB2mVQ)中,一些同学可能为了处理逻辑:每隔2k个字符的前k的字符,写了一堆逻辑代码或者再搞一个计数器,来统计2k,再统计前k个字符。 其实**当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章**。 @@ -80,34 +81,34 @@ for (int i = 0; i < a.size(); i++) { 因为要找的也就是每2 * k 区间的起点,这样写程序会高效很多。 -在[字符串:花式反转还不够!](https://mp.weixin.qq.com/s/X3qpi2v5RSp08mO-W5Vicw)中要求翻转字符串里的单词,这道题目可以说是综合考察了字符串的多种操作。是考察字符串的好题。 +在[151.翻转字符串里的单词](https://mp.weixin.qq.com/s/4j6vPFHkFAXnQhmSkq2X9g)中要求翻转字符串里的单词,这道题目可以说是综合考察了字符串的多种操作。是考察字符串的好题。 这道题目通过 **先整体反转再局部反转**,实现了反转字符串里的单词。 后来发现反转字符串还有一个牛逼的用处,就是达到左旋的效果。 -在[字符串:反转个字符串还有这个用处?](https://mp.weixin.qq.com/s/PmcdiWSmmccHAONzU0ScgQ)中,我们通过**先局部反转再整体反转**达到了左旋的效果。 +在[字符串:反转个字符串还有这个用处?](https://mp.weixin.qq.com/s/Px_L-RfT2b_jXKcNmccPsw)中,我们通过**先局部反转再整体反转**达到了左旋的效果。 # KMP KMP的主要思想是**当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。** -KMP的精髓所在就是前缀表,在[字符串:KMP是时候上场了(一文读懂系列)](https://mp.weixin.qq.com/s/70OXnZ4Ez29CKRrUpVJmug)中提到了,什么是KMP,什么是前缀表,以及为什么要用前缀表。 +KMP的精髓所在就是前缀表,在[KMP精讲](https://mp.weixin.qq.com/s/MoRBHbS4hQXn7LcPdmHmIg)中提到了,什么是KMP,什么是前缀表,以及为什么要用前缀表。 前缀表:起始位置到下表i之前(包括i)的子串中,有多大长度的相同前缀后缀。 那么使用KMP可以解决两类经典问题: -1. 匹配问题:[28. 实现 strStr()](https://mp.weixin.qq.com/s/Gk9FKZ9_FSWLEkdGrkecyg) -2. 重复子串问题:[459.重复的子字符串](https://mp.weixin.qq.com/s/lR2JPtsQSR2I_9yHbBmBuQ) +1. 匹配问题:[28. 实现 strStr()](https://mp.weixin.qq.com/s/MoRBHbS4hQXn7LcPdmHmIg) +2. 重复子串问题:[459.重复的子字符串](https://mp.weixin.qq.com/s/32Pve4j8IWvdgxYEZdTeFg) -在[字符串:听说你对KMP有这些疑问?](https://mp.weixin.qq.com/s/mqx6IM2AO4kLZwvXdPtEeQ) 强调了什么是前缀,什么是后缀,什么又是最长相等前后缀。 +再一次强调了什么是前缀,什么是后缀,什么又是最长相等前后缀。 前缀:指不包含最后一个字符的所有以第一个字符开头的连续子串。 后缀:指不包含第一个字符的所有以最后一个字符结尾的连续子串。 -然后**针对前缀表到底要不要减一,这其实是不同KMP实现的方式**,我们在[字符串:前缀表不右移,难道就写不出KMP了?](https://mp.weixin.qq.com/s/p3hXynQM2RRROK5c6X7xfw)中针对之前两个问题,分别给出了两个不同版本的的KMP实现。 +然后**针对前缀表到底要不要减一,这其实是不同KMP实现的方式**,我们在[KMP精讲](https://mp.weixin.qq.com/s/MoRBHbS4hQXn7LcPdmHmIg)中针对之前两个问题,分别给出了两个不同版本的的KMP实现。 其中主要**理解j=next[x]这一步最为关键!** @@ -123,18 +124,6 @@ KMP算法是字符串查找最重要的算法,但彻底理解KMP并不容易 -## 其他语言版本 - - -Java: - - -Python: - - -Go: - - ----------------------- diff --git a/problems/栈与队列理论基础.md b/problems/栈与队列理论基础.md index 04f99981..db871a3c 100644 --- a/problems/栈与队列理论基础.md +++ b/problems/栈与队列理论基础.md @@ -85,20 +85,7 @@ std::queue> third; // 定义以list为底层容器的队列 所以STL 队列也不被归类为容器,而被归类为container adapter( 容器适配器)。 -我这里讲的都是(clck)C++ 语言中情况, 使用其他语言的同学也要思考栈与队列的底层实现问题, 不要对数据结构的使用浅尝辄止,而要深挖起内部原理,才能夯实基础。 - - - -## 其他语言版本 - - -Java: - - -Python: - - -Go: +我这里讲的都是C++ 语言中情况, 使用其他语言的同学也要思考栈与队列的底层实现问题, 不要对数据结构的使用浅尝辄止,而要深挖起内部原理,才能夯实基础。 diff --git a/problems/背包理论基础01背包-2.md b/problems/背包理论基础01背包-2.md index e85d31b4..48275908 100644 --- a/problems/背包理论基础01背包-2.md +++ b/problems/背包理论基础01背包-2.md @@ -5,6 +5,7 @@

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

+ # 动态规划:关于01背包问题,你该了解这些!(滚动数组) 昨天[动态规划:关于01背包问题,你该了解这些!](https://mp.weixin.qq.com/s/FwIiPPmR18_AJO5eiidT6w)中是用二维dp数组来讲解01背包。 @@ -35,7 +36,7 @@ **其实可以发现如果把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);** -**于其把dp[i - 1]这一层拷贝到dp[i]上,不如只用一个一维数组了**,只用dp[j](一维数组,也可以理解是一个滚动数组)。 +**与其把dp[i - 1]这一层拷贝到dp[i]上,不如只用一个一维数组了**,只用dp[j](一维数组,也可以理解是一个滚动数组)。 这就是滚动数组的由来,需要满足的条件是上一层可以重复利用,直接拷贝到当前层。 @@ -214,7 +215,7 @@ int main() { Java: ```java - public static void main(String[] args) { + public static void main(String[] args) { int[] weight = {1, 3, 4}; int[] value = {15, 20, 30}; int bagWight = 4; @@ -242,7 +243,24 @@ Java: 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) + # 先遍历物品, 再遍历背包容量 + for i in range(len(weight)): + for j in range(bag_weight, weight[i] - 1, -1): + # 递归公式 + dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) + + print(dp) + +test_1_wei_bag_problem() +``` Go: ```go