diff --git a/README.md b/README.md index 276c8313..7eba656d 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,17 @@ 很多刚开始刷题的同学都有一个困惑:面对leetcode上近两千道题目,从何刷起。 +大家平时刷题感觉效率低,浪费的时间主要在三点: + +* 找题 +* 找到了不应该现阶段做的题 +* 没有全套的优质题解可以参考 + 其实我之前在知乎上回答过这个问题,回答内容大概是按照如下类型来刷数组-> 链表-> 哈希表->字符串->栈与队列->树->回溯->贪心->动态规划->图论->高级数据结构,再从简单刷起,做了几个类型题目之后,再慢慢做中等题目、困难题目。 但我能设身处地的感受到:即使有这样一个整体规划,对于一位初学者甚至算法老手寻找合适自己的题目也是很困难,时间成本很高,而且题目还不一定就是经典题目。 -对于刷题,我们都是想用最短的时间把经典题目都做一篇,这样效率才是最高的! +对于刷题,我们都是想用最短的时间**按照循序渐进的难度顺序把经典题目都做一遍**,这样效率才是最高的! 所以我整理了leetcode刷题攻略:一个超级详细的刷题顺序,**每道题目都是我精心筛选,都是经典题目高频面试题**,大家只要按照这个顺序刷就可以了,**你没看错,就是题目顺序都排好了,文章顺序就是刷题顺序!挨个刷就可以,不用自己再去题海里选题了!** @@ -49,15 +55,18 @@ **目前已经更新了,数组-> 链表-> 哈希表->字符串->栈与队列->树->回溯->贪心,八个专题了,正在讲解动态规划!** -在刷题指南中,每个专题开始都有理论基础篇,并不像是教科书般的理论介绍,而是从实战中归纳需要的基础知识。每个专题结束都有总结篇,最这个专题的归纳总结。 +在刷题攻略中,每个专题开始都有理论基础篇,并不像是教科书般的理论介绍,而是从实战中归纳需要的基础知识。每个专题结束都有总结篇,最这个专题的归纳总结。 如果你是算法老手,这篇攻略也是复习的最佳资料,如果把每个系列对应的总结篇,快速过一遍,整个算法知识体系以及各种解法就重现脑海了。 -在按照如下顺序刷题的过程中,每一道题解一定要看对应文章下面的留言(留言目前只能在手机端查看)。 -如果你有疑问或者发现文章哪里有不对的地方,都可以在留言区都能找到答案,还有很多录友的总结非常赞,看完之后也很有收获。 +目前「代码随想录」刷题攻略更新了:**200多篇文章,精讲了200道经典算法题目,共60w字的详细图解,部分难点题目还搭配了20分钟左右的视频讲解**。 -目前「代码随想录」刷题指南更新了:**200多篇文章,精讲了200道经典算法题目,共60w字的详细图解,部分难点题目还搭配了20分钟左右的视频讲解**。 +**这里每一篇题解,都是精品,值得仔细琢磨**。 + +我在题目讲解中统一用C++语言,但你会发现下面几乎每篇题解都配有其他语言版本,Java、Python、Go、JavaScript等等,这正是热心小伙们的贡献的代码,当然我也会严格把控代码质量。 + +**所以也欢迎大家参与进来,完善题解的各个语言版本,拥抱开源,让更多小伙伴们收益**。 准备好了么,刷题攻略开始咯,go go go! @@ -66,7 +75,7 @@ ## 前序 * [「代码随想录」后序安排](https://mp.weixin.qq.com/s/4eeGJREy6E-v6D7cR_5A4g) -* [「代码随想录」学习社区](https://mp.weixin.qq.com/s/X1XCH-KevURi3LnakJsCkA) +* [「代码随想录」学习社区](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) * 编程语言 @@ -119,8 +128,9 @@ 4. [链表:听说过两天反转链表又写不出来了?](./problems/0206.翻转链表.md) 5. [链表:两两交换链表中的节点](./problems/0024.两两交换链表中的节点.md) 6. [链表:删除链表的倒数第 N 个结点](./problems/0019.删除链表的倒数第N个节点.md) -7. [链表:环找到了,那入口呢?](./problems/0142.环形链表II.md) -8. [链表:总结篇!](./problems/链表总结篇.md) +7. [链表:链表相交](./problems/面试题02.07.链表相交.md) +8. [链表:环找到了,那入口呢?](./problems/0142.环形链表II.md) +9. [链表:总结篇!](./problems/链表总结篇.md) ## 哈希表 @@ -156,11 +166,13 @@ 3. [字符串:替换空格](./problems/剑指Offer05.替换空格.md) 4. [字符串:花式反转还不够!](./problems/0151.翻转字符串里的单词.md) 5. [链表:听说过两天反转链表又写不出来了?](./problems/0206.翻转链表.md) -6. [链表:环找到了,那入口呢?](./problems/0142.环形链表II.md) -7. [链表:删除链表的倒数第 N 个结点](./problems/0019.删除链表的倒数第N个节点.md) -8. [哈希表:解决了两数之和,那么能解决三数之和么?](./problems/0015.三数之和.md) -9. [双指针法:一样的道理,能解决四数之和](./problems/0018.四数之和.md) -10. [双指针法:总结篇!](./problems/双指针总结.md) +6. [链表:删除链表的倒数第 N 个结点](./problems/0019.删除链表的倒数第N个节点.md) +7. [链表:链表相交](./problems/面试题02.07.链表相交.md) +8. [链表:环找到了,那入口呢?](./problems/0142.环形链表II.md) +9. [链表:删除链表的倒数第 N 个结点](./problems/0019.删除链表的倒数第N个节点.md) +10. [哈希表:解决了两数之和,那么能解决三数之和么?](./problems/0015.三数之和.md) +11. [双指针法:一样的道理,能解决四数之和](./problems/0018.四数之和.md) +12. [双指针法:总结篇!](./problems/双指针总结.md) ## 栈与队列 diff --git a/problems/0020.有效的括号.md b/problems/0020.有效的括号.md index 77c6e10a..98cc7cd8 100644 --- a/problems/0020.有效的括号.md +++ b/problems/0020.有效的括号.md @@ -162,6 +162,33 @@ class Solution { return deque.isEmpty(); } } +// 方法2 +class Solution { + public boolean isValid(String s) { + + Stack stack = new Stack<>(); + Map map = new HashMap() { + { + put('}', '{'); + put(']', '['); + put(')', '('); + } + }; + + for (Character c : s.toCharArray()) { // 顺序读取字符 + if (!stack.isEmpty() && map.containsKey(c)) { // 是右括号 && 栈不为空 + if (stack.peek() == map.get(c)) { // 取其对应的左括号直接和栈顶比 + stack.pop(); // 相同则抵消,出栈 + } else { + return false; // 不同则直接返回 + } + } else { + stack.push(c); // 左括号,直接入栈 + } + } + return stack.isEmpty(); // 看左右是否抵消完 + } +} ``` Python: diff --git a/problems/0024.两两交换链表中的节点.md b/problems/0024.两两交换链表中的节点.md index 9f07b4f7..132d65bb 100644 --- a/problems/0024.两两交换链表中的节点.md +++ b/problems/0024.两两交换链表中的节点.md @@ -87,6 +87,46 @@ public: Java: +```Java +// 递归版本 +class Solution { + public ListNode swapPairs(ListNode head) { + // base case 退出提交 + if(head == null || head.next == null) return head; + // 获取当前节点的下一个节点 + ListNode next = head.next; + // 进行递归 + ListNode newNode = swapPairs(next.next); + // 这里进行交换 + next.next = head; + head.next = newNode; + + return next; + } +} +``` + +```java +// 虚拟头结点 +class Solution { + public ListNode swapPairs(ListNode head) { + + ListNode dummyNode = new ListNode(0); + dummyNode.next = head; + ListNode prev = dummyNode; + + while (prev.next != null && prev.next.next != null) { + ListNode temp = head.next.next; // 缓存 next + prev.next = head.next; // 将 prev 的 next 改为 head 的 next + head.next.next = head; // 将 head.next(prev.next) 的next,指向 head + head.next = temp; // 将head 的 next 接上缓存的temp + prev = head; // 步进1位 + head = head.next; // 步进1位 + } + return dummyNode.next; + } +} +``` Python: diff --git a/problems/0035.搜索插入位置.md b/problems/0035.搜索插入位置.md index e891e3c5..a11912b8 100644 --- a/problems/0035.搜索插入位置.md +++ b/problems/0035.搜索插入位置.md @@ -116,7 +116,7 @@ public: **大家要仔细看注释,思考为什么要写while(left <= right), 为什么要写right = middle - 1**。 -``` +```C++ class Solution { public: int searchInsert(vector& nums, int target) { @@ -158,7 +158,7 @@ public: **大家要仔细看注释,思考为什么要写while (left < right), 为什么要写right = middle**。 -``` +```C++ class Solution { public: int searchInsert(vector& nums, int target) { diff --git a/problems/0045.跳跃游戏II.md b/problems/0045.跳跃游戏II.md index b8e369e6..f65ceb9e 100644 --- a/problems/0045.跳跃游戏II.md +++ b/problems/0045.跳跃游戏II.md @@ -175,7 +175,22 @@ 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 +``` Go: diff --git a/problems/0046.全排列.md b/problems/0046.全排列.md index b3ede237..5f5aea1f 100644 --- a/problems/0046.全排列.md +++ b/problems/0046.全排列.md @@ -149,6 +149,7 @@ public: Java: ```java class Solution { + List> result = new ArrayList<>();// 存放符合条件结果的集合 LinkedList path = new LinkedList<>();// 用来存放符合条件结果 boolean[] used; @@ -167,9 +168,6 @@ class Solution { return; } for (int i = 0; i < nums.length; i++){ - // if (path.contains(nums[i])){ - // continue; - // } if (used[i]){ continue; } @@ -187,6 +185,26 @@ Python: Go: +```Go +var result [][]int +func backtrack(nums,pathNums []int,used []bool){ + if len(nums)==len(pathNums){ + tmp:=make([]int,len(nums)) + copy(tmp,pathNums) + result=append(result,tmp) + //result=append(result,pathNums) + return + } + for i:=0;i used(nums.size(), false); - backtracking(nums, vec, used); + backtracking(nums, used); return result; } }; diff --git a/problems/0051.N皇后.md b/problems/0051.N皇后.md index ac0235c2..935ea89c 100644 --- a/problems/0051.N皇后.md +++ b/problems/0051.N皇后.md @@ -363,7 +363,72 @@ Python: Go: +```Go +import "strings" +var res [][]string +func isValid(board [][]string, row, col int) (res bool){ + n := len(board) + for i:=0; i < row; i++ { + if board[i][col] == "Q" { + return false + } + } + for i := 0; i < n; i++{ + if board[row][i] == "Q" { + return false + } + } + + for i ,j := row, col; i >= 0 && j >=0 ; i, j = i - 1, j- 1{ + if board[i][j] == "Q"{ + return false + } + } + for i, j := row, col; i >=0 && j < n; i,j = i-1, j+1 { + if board[i][j] == "Q" { + return false + } + } + return true +} + +func backtrack(board [][]string, row int) { + size := len(board) + if row == size{ + temp := make([]string, size) + for i := 0; ib{ - return a - }else{ - return b +```go +func maxSubArray(nums []int) int { + maxSum := nums[0] + for i := 1; i < len(nums); i++ { + if nums[i] + nums[i-1] > nums[i] { + nums[i] += nums[i-1] + } + if nums[i] > maxSum { + maxSum = nums[i] + } } + return maxSum } ``` + + ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) * B站视频:[代码随想录](https://space.bilibili.com/525438321) diff --git a/problems/0070.爬楼梯.md b/problems/0070.爬楼梯.md index f0a08f99..6cbd6299 100644 --- a/problems/0070.爬楼梯.md +++ b/problems/0070.爬楼梯.md @@ -212,7 +212,45 @@ public: Java: +```Java +class Solution { + public int climbStairs(int n) { + // 跟斐波那契数列一样 + if(n <= 2) return n; + int a = 1, b = 2, sum = 0; + + for(int i = 3; i <= n; i++){ + sum = a + b; + a = b; + b = sum; + } + return b; + } +} +``` +```java +// 常规方式 +public int climbStairs(int n) { + int[] dp = new int[n + 1]; + dp[0] = 1; + dp[1] = 1; + for (int i = 2; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; +} +// 用变量记录代替数组 +public int climbStairs(int n) { + int a = 0, b = 1, c = 0; // 默认需要1次 + for (int i = 1; i <= n; i++) { + c = a + b; // f(i - 1) + f(n - 2) + a = b; // 记录上一轮的值 + b = c; // 向后步进1个数 + } + return c; +} +``` Python: diff --git a/problems/0072.编辑距离.md b/problems/0072.编辑距离.md index 8e6e0187..32c888f1 100644 --- a/problems/0072.编辑距离.md +++ b/problems/0072.编辑距离.md @@ -198,13 +198,71 @@ public: Java: - +```java +public int minDistance(String word1, String word2) { + int m = word1.length(); + int n = word2.length(); + int[][] dp = new int[m + 1][n + 1]; + // 初始化 + for (int i = 1; i <= m; i++) { + dp[i][0] = i; + } + for (int j = 1; j <= n; j++) { + dp[0][j] = j; + } + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + // 因为dp数组有效位从1开始 + // 所以当前遍历到的字符串的位置为i-1 | j-1 + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = Math.min(Math.min(dp[i - 1][j - 1], dp[i][j - 1]), dp[i - 1][j]) + 1; + } + } + } + return dp[m][n]; +} +``` Python: Go: - +```Go +func minDistance(word1 string, word2 string) int { + m, n := len(word1), len(word2) + dp := make([][]int, m+1) + for i := range dp { + dp[i] = make([]int, n+1) + } + for i := 0; i < m+1; i++ { + dp[i][0] = i // word1[i] 变成 word2[0], 删掉 word1[i], 需要 i 部操作 + } + for j := 0; j < n+1; j++ { + dp[0][j] = j // word1[0] 变成 word2[j], 插入 word1[j],需要 j 部操作 + } + for i := 1; i < m+1; i++ { + for j := 1; j < n+1; j++ { + if word1[i-1] == word2[j-1] { + dp[i][j] = dp[i-1][j-1] + } else { // Min(插入,删除,替换) + dp[i][j] = Min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1 + } + } + } + return dp[m][n] +} +func Min(args ...int) int { + min := args[0] + for _, item := range args { + if item < min { + min = item + } + } + return min +} +``` diff --git a/problems/0077.组合.md b/problems/0077.组合.md index f31766e0..1f0d6eb1 100644 --- a/problems/0077.组合.md +++ b/problems/0077.组合.md @@ -373,6 +373,32 @@ Python: Go: +```Go +var res [][]int +func combine(n int, k int) [][]int { + res=[][]int{} + if n <= 0 || k <= 0 || k > n { + return res + } + backtrack(n, k, 1, []int{}) + return res +} +func backtrack(n,k,start int,track []int){ + if len(track)==k{ + temp:=make([]int,k) + copy(temp,track) + res=append(res,temp) + } + if len(track)+n-start+1 < k { + return + } + for i:=start;i<=n;i++{ + track=append(track,i) + backtrack(n,k,i+1,track) + track=track[:len(track)-1] + } +} +``` diff --git a/problems/0096.不同的二叉搜索树.md b/problems/0096.不同的二叉搜索树.md index 7dea8fb0..dca225bf 100644 --- a/problems/0096.不同的二叉搜索树.md +++ b/problems/0096.不同的二叉搜索树.md @@ -189,7 +189,18 @@ Python: Go: - +```Go +func numTrees(n int)int{ + dp:=make([]int,n+1) + dp[0]=1 + for i:=1;i<=n;i++{ + for j:=1;j<=i;j++{ + dp[i]+=dp[j-1]*dp[i-j] + } + } + return dp[n] +} +``` diff --git a/problems/0098.验证二叉搜索树.md b/problems/0098.验证二叉搜索树.md index f246c21a..d8945eff 100644 --- a/problems/0098.验证二叉搜索树.md +++ b/problems/0098.验证二叉搜索树.md @@ -304,6 +304,35 @@ class Solution { return true; } } + +// 简洁实现·递归解法 +class Solution { + public boolean isValidBST(TreeNode root) { + return validBST(Long.MIN_VALUE, Long.MAX_VALUE, root); + } + boolean validBST(long lower, long upper, TreeNode root) { + if (root == null) return true; + if (root.val <= lower || root.val >= upper) return false; + return validBST(lower, root.val, root.left) && validBST(root.val, upper, root.right); + } +} +// 简洁实现·中序遍历 +class Solution { + private long prev = Long.MIN_VALUE; + public boolean isValidBST(TreeNode root) { + if (root == null) { + return true; + } + if (!isValidBST(root.left)) { + return false; + } + if (root.val <= prev) { // 不满足二叉搜索树条件 + return false; + } + prev = root.val; + return isValidBST(root.right); + } +} ``` Python: diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md index 9b5f9ed5..0ab67b13 100644 --- a/problems/0102.二叉树的层序遍历.md +++ b/problems/0102.二叉树的层序遍历.md @@ -657,8 +657,36 @@ Python: Go: +```Go +func levelOrder(root *TreeNode) [][]int { + result:=make([][]int,0) + if root==nil{ + return result + } + queue:=make([]*TreeNode,0) + queue=append(queue,root) + for len(queue)>0{ + list:=make([]int,0) + l:=len(queue) + + for i:=0;i number === root.val) + + root.left = buildTree(inorder.slice(0, index), postorder.slice(0, index)) + root.right = buildTree(inorder.slice(index + 1, inorder.length), postorder.slice(index, postorder.length - 1)) + + return root +}; +``` ----------------------- * 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) diff --git a/problems/0112.路径总和.md b/problems/0112.路径总和.md index 1b75113e..ecbefd90 100644 --- a/problems/0112.路径总和.md +++ b/problems/0112.路径总和.md @@ -332,6 +332,19 @@ class Solution { } } +// LC112 简洁方法 +class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + + if (root == null) return false; // 为空退出 + + // 叶子节点判断是否符合 + if (root.left == null && root.right == null) return root.val == targetSum; + + // 求两侧分支的路径和 + return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val); + } +} ``` Python: diff --git a/problems/0121.买卖股票的最佳时机.md b/problems/0121.买卖股票的最佳时机.md index 3d564892..ec277263 100644 --- a/problems/0121.买卖股票的最佳时机.md +++ b/problems/0121.买卖股票的最佳时机.md @@ -198,12 +198,52 @@ public: Java: - +```java +class Solution { + public int maxProfit(int[] prices) { + int minprice = Integer.MAX_VALUE; + int maxprofit = 0; + for (int i = 0; i < prices.length; i++) { + if (prices[i] < minprice) { + minprice = prices[i]; + } else if (prices[i] - minprice > maxprofit) { + maxprofit = prices[i] - minprice; + } + } + return maxprofit; + } +} +``` Python: Go: +```Go +func maxProfit(prices []int) int { + length:=len(prices) + if length==0{return 0} + dp:=make([][]int,length) + for i:=0;ib{ + return a + } + return b +} +``` diff --git a/problems/0122.买卖股票的最佳时机II(动态规划).md b/problems/0122.买卖股票的最佳时机II(动态规划).md index 3444ca73..e3526ecf 100644 --- a/problems/0122.买卖股票的最佳时机II(动态规划).md +++ b/problems/0122.买卖股票的最佳时机II(动态规划).md @@ -133,7 +133,41 @@ public: Java: +```java +// 动态规划 +class Solution + // 实现1:二维数组存储 + // 可以将每天持有与否的情况分别用 dp[i][0] 和 dp[i][1] 来进行存储 + // 时间复杂度:O(n),空间复杂度O(n) + public int maxProfit(int[] prices) { + int n = prices.length; + int[][] dp = new int[n][2]; // 创建二维数组存储状态 + dp[0][0] = 0; // 初始状态 + dp[0][1] = -prices[0]; + for (int i = 1; i < n; ++i) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]); // 第 i 天,没有股票 + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]); // 第 i 天,持有股票 + } + return dp[n - 1][0]; // 卖出股票收益高于持有股票收益,因此取[0] + } + // 实现2:变量存储 + // 第一种方法需要用二维数组存储,有空间开销,其实关心的仅仅是前一天的状态,不关注更多的历史信息 + // 因此,可以仅保存前一天的信息存入 dp0、dp1 这 2 个变量即可 + // 时间复杂度:O(n),空间复杂度O(1) + public int maxProfit(int[] prices) { + int n = prices.length; + int dp0 = 0, dp1 = -prices[0]; // 定义变量,存储初始状态 + for (int i = 1; i < n; ++i) { + int newDp0 = Math.max(dp0, dp1 + prices[i]); // 第 i 天,没有股票 + int newDp1 = Math.max(dp1, dp0 - prices[i]); // 第 i 天,持有股票 + dp0 = newDp0; + dp1 = newDp1; + } + return dp0; + } +} +``` Python: diff --git a/problems/0134.加油站.md b/problems/0134.加油站.md index 393e4627..52019aec 100644 --- a/problems/0134.加油站.md +++ b/problems/0134.加油站.md @@ -223,7 +223,21 @@ class Solution { ``` Python: - +```python +class Solution: + def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: + start = 0 + curSum = 0 + totalSum = 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 + return start +``` Go: @@ -234,4 +248,4 @@ Go: * 作者微信:[程序员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/0135.分发糖果.md b/problems/0135.分发糖果.md index fedf8765..51b32965 100644 --- a/problems/0135.分发糖果.md +++ b/problems/0135.分发糖果.md @@ -161,7 +161,18 @@ class Solution { ``` Python: - +```python +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) +``` Go: @@ -172,4 +183,4 @@ Go: * 作者微信:[程序员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/0150.逆波兰表达式求值.md b/problems/0150.逆波兰表达式求值.md index 4e7365f7..12977c39 100644 --- a/problems/0150.逆波兰表达式求值.md +++ b/problems/0150.逆波兰表达式求值.md @@ -170,7 +170,32 @@ public class EvalRPN { } ``` - +Go: +```Go +func evalRPN(tokens []string) int { + stack := []int{} + for _, token := range tokens { + val, err := strconv.Atoi(token) + if err == nil { + stack = append(stack, val) + } else { + num1, num2 := stack[len(stack)-2], stack[(len(stack))-1] + stack = stack[:len(stack)-2] + switch token { + case "+": + stack = append(stack, num1+num2) + case "-": + stack = append(stack, num1-num2) + case "*": + stack = append(stack, num1*num2) + case "/": + stack = append(stack, num1/num2) + } + } + } + return stack[0] +} +``` diff --git a/problems/0151.翻转字符串里的单词.md b/problems/0151.翻转字符串里的单词.md index d9ecfc12..62a47ab7 100644 --- a/problems/0151.翻转字符串里的单词.md +++ b/problems/0151.翻转字符串里的单词.md @@ -141,7 +141,8 @@ void reverse(string& s, int start, int end) { -``` +```C++ +// 版本一 class Solution { public: // 反转字符串s中左闭又闭的区间[start, end] @@ -182,7 +183,7 @@ public: int end = 0; // 反转的单词在字符串里终止位置 bool entry = false; // 标记枚举字符串的过程中是否已经进入了单词区间 for (int i = 0; i < s.size(); i++) { // 开始反转单词 - if ((!entry) || (s[i] != ' ' && s[i - 1] == ' ')) { + if ((!entry))) { start = i; // 确定单词起始位置 entry = true; // 进入单词区间 } @@ -201,9 +202,42 @@ public: } return s; } + + /* 主函数简单写法 + 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; + } + */ }; ``` +当然这里的主函数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; +} +``` + diff --git a/problems/0198.打家劫舍.md b/problems/0198.打家劫舍.md index c64648ad..649dd055 100644 --- a/problems/0198.打家劫舍.md +++ b/problems/0198.打家劫舍.md @@ -111,7 +111,24 @@ public: Java: +```Java +// 动态规划 +class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) return 0; + if (nums.length == 1) return nums[0]; + int[] dp = new int[nums.length + 1]; + dp[0] = nums[0]; + dp[1] = Math.max(dp[0], nums[1]); + for (int i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); + } + + return dp[nums.length - 1]; + } +} +``` Python: diff --git a/problems/0213.打家劫舍II.md b/problems/0213.打家劫舍II.md index 55e71bc0..fc58a065 100644 --- a/problems/0213.打家劫舍II.md +++ b/problems/0213.打家劫舍II.md @@ -98,7 +98,28 @@ public: Java: +```Java +class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) + return 0; + int len = nums.length; + if (len == 1) + return nums[0]; + return Math.max(robAction(nums, 0, len - 1), robAction(nums, 1, len)); + } + int robAction(int[] nums, int start, int end) { + int x = 0, y = 0, z = 0; + for (int i = start; i < end; i++) { + y = z; + z = Math.max(y, x + nums[i]); + x = y; + } + return z; + } +} +``` Python: diff --git a/problems/0236.二叉树的最近公共祖先.md b/problems/0236.二叉树的最近公共祖先.md index 69127bd9..3233c6a1 100644 --- a/problems/0236.二叉树的最近公共祖先.md +++ b/problems/0236.二叉树的最近公共祖先.md @@ -266,7 +266,34 @@ Python: Go: +```Go +func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { + // check + if root == nil { + return root + } + // 相等 直接返回root节点即可 + if root == p || root == q { + return root + } + // Divide + left := lowestCommonAncestor(root.Left, p, q) + right := lowestCommonAncestor(root.Right, p, q) + // Conquer + // 左右两边都不为空,则根节点为祖先 + if left != nil && right != nil { + return root + } + if left != nil { + return left + } + if right != nil { + return right + } + return nil +} +``` @@ -274,4 +301,4 @@ Go: * 作者微信:[程序员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/0257.二叉树的所有路径.md b/problems/0257.二叉树的所有路径.md index 73387257..29bcdf41 100644 --- a/problems/0257.二叉树的所有路径.md +++ b/problems/0257.二叉树的所有路径.md @@ -77,7 +77,7 @@ if (cur->left == NULL && cur->right == NULL) { 这里我们先使用vector结构的path容器来记录路径,那么终止处理逻辑如下: -``` +```C++ if (cur->left == NULL && cur->right == NULL) { // 遇到叶子节点 string sPath; for (int i = 0; i < path.size() - 1; i++) { // 将path里记录的路径转为string格式 @@ -113,7 +113,7 @@ if (cur->right) { 那么回溯要怎么回溯呢,一些同学会这么写,如下: -``` +```C++ if (cur->left) { traversal(cur->left, path, result); } @@ -129,7 +129,7 @@ path.pop_back(); 那么代码应该这么写: -``` +```C++ if (cur->left) { traversal(cur->left, path, result); path.pop_back(); // 回溯 @@ -335,4 +335,4 @@ Go: * 作者微信:[程序员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/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md index 2bc5cf9c..f3b51588 100644 --- a/problems/0300.最长上升子序列.md +++ b/problems/0300.最长上升子序列.md @@ -98,8 +98,6 @@ public: }; ``` -杨老师的这个专栏很不错,他本身也是Oracle 首席工程师,对Java有极其深刻的理解,讲的内容很硬核,适合使用Java语言的录友们用来进阶!作为面试突击手册非常合适, 所以推荐给大家!现在下单输入口令:javahexin,可以省40元那[机智] - ## 总结 本题最关键的是要想到dp[i]由哪些状态可以推出来,并取最大值,那么很自然就能想到递推公式:dp[i] = max(dp[i], dp[j] + 1); @@ -110,14 +108,58 @@ public: Java: - +```Java +class Solution { + public int lengthOfLIS(int[] nums) { + int[] dp = new int[nums.length]; + Arrays.fill(dp, 1); + for (int i = 0; i < dp.length; i++) { + for (int j = 0; j < i; j++) { + if (nums[i] > nums[j]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + } + int res = 0; + for (int i = 0; i < dp.length; i++) { + res = Math.max(res, dp[i]); + } + return res; + } +} +``` Python: Go: - - +```go +func lengthOfLIS(nums []int ) int { + dp := []int{} + for _, num := range nums { + if len(dp) ==0 || dp[len(dp) - 1] < num { + dp = append(dp, num) + } else { + l, r := 0, len(dp) - 1 + pos := r + for l <= r { + mid := (l + r) >> 1 + if dp[mid] >= num { + pos = mid; + r = mid - 1 + } else { + l = mid + 1 + } + } + dp[pos] = num + }//二分查找 + } + return len(dp) +} +``` +*复杂度分析* +- 时间复杂度:O(nlogn)。数组 nums 的长度为 n,我们依次用数组中的元素去更新 dp 数组,相当于插入最后递增的元素,而更新 dp 数组时需要进行 O(logn) 的二分搜索,所以总时间复杂度为 O(nlogn)。 +- 空间复杂度:O(n),需要额外使用长度为 n 的 dp 数组。 ----------------------- diff --git a/problems/0337.打家劫舍III.md b/problems/0337.打家劫舍III.md index 50b06e22..20eea817 100644 --- a/problems/0337.打家劫舍III.md +++ b/problems/0337.打家劫舍III.md @@ -218,7 +218,72 @@ public: Java: +```Java +class Solution { + // 1.递归去偷,超时 + public int rob(TreeNode root) { + if (root == null) + return 0; + int money = root.val; + if (root.left != null) { + money += rob(root.left.left) + rob(root.left.right); + } + if (root.right != null) { + money += rob(root.right.left) + rob(root.right.right); + } + return Math.max(money, rob(root.left) + rob(root.right)); + } + // 2.递归去偷,记录状态 + // 执行用时:3 ms , 在所有 Java 提交中击败了 56.24% 的用户 + public int rob1(TreeNode root) { + Map memo = new HashMap<>(); + return robAction(root, memo); + } + + int robAction(TreeNode root, Map memo) { + if (root == null) + return 0; + if (memo.containsKey(root)) + return memo.get(root); + int money = root.val; + if (root.left != null) { + money += robAction(root.left.left, memo) + robAction(root.left.right, memo); + } + if (root.right != null) { + money += robAction(root.right.left, memo) + robAction(root.right.right, memo); + } + int res = Math.max(money, robAction(root.left, memo) + robAction(root.right, memo)); + memo.put(root, res); + return res; + } + + // 3.状态标记递归 + // 执行用时:0 ms , 在所有 Java 提交中击败了 100% 的用户 + // 不偷:Max(左孩子不偷,左孩子偷) + Max(又孩子不偷,右孩子偷) + // root[0] = Math.max(rob(root.left)[0], rob(root.left)[1]) + + // Math.max(rob(root.right)[0], rob(root.right)[1]) + // 偷:左孩子不偷+ 右孩子不偷 + 当前节点偷 + // root[1] = rob(root.left)[0] + rob(root.right)[0] + root.val; + public int rob3(TreeNode root) { + int[] res = robAction1(root); + return Math.max(res[0], res[1]); + } + + int[] robAction1(TreeNode root) { + int res[] = new int[2]; + if (root == null) + return res; + + int[] left = robAction1(root.left); + int[] right = robAction1(root.right); + + res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]); + res[1] = root.val + left[0] + right[0]; + return res; + } +} +``` Python: diff --git a/problems/0509.斐波那契数.md b/problems/0509.斐波那契数.md index 8537ed8b..8be11312 100644 --- a/problems/0509.斐波那契数.md +++ b/problems/0509.斐波那契数.md @@ -171,7 +171,20 @@ public: Java: - +```Java +class Solution { + public int fib(int n) { + if (n < 2) return n; + int a = 0, b = 1, c = 0; + for (int i = 1; i < n; i++) { + c = a + b; + a = b; + b = c; + } + return c; + } +} +``` Python: diff --git a/problems/0647.回文子串.md b/problems/0647.回文子串.md index fbc9133e..45bb72be 100644 --- a/problems/0647.回文子串.md +++ b/problems/0647.回文子串.md @@ -227,6 +227,30 @@ Python: Go: +```Go +func countSubstrings(s string) int { + res:=0 + dp:=make([][]bool,len(s)) + for i:=0;i=0;i--{ + for j:=i;j val { + root.Left = insertIntoBST(root.Left, val) + } else { + root.Right = insertIntoBST(root.Right, val) + } + return root +} +``` @@ -279,4 +293,4 @@ Go: * 作者微信:[程序员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/1005.K次取反后最大化的数组和.md b/problems/1005.K次取反后最大化的数组和.md index 1653201e..89ed9176 100644 --- a/problems/1005.K次取反后最大化的数组和.md +++ b/problems/1005.K次取反后最大化的数组和.md @@ -124,7 +124,18 @@ 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[i] *= -1 + K -= 1 + if K > 0: + A[len(A) - 1] *= ((-1)**K) + return sum(A) +``` Go: @@ -135,4 +146,4 @@ Go: * 作者微信:[程序员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 02d4b060..e8cf0bdd 100644 --- a/problems/二叉树的递归遍历.md +++ b/problems/二叉树的递归遍历.md @@ -115,7 +115,59 @@ void traversal(TreeNode* cur, vector& vec) { Java: +```Java +// 前序遍历·递归·LC144_二叉树的前序遍历 +class Solution { + ArrayList preOrderReverse(TreeNode root) { + ArrayList result = new ArrayList(); + preOrder(root, result); + return result; + } + void preOrder(TreeNode root, ArrayList result) { + if (root == null) { + return; + } + result.add(root.val); // 注意这一句 + preOrder(root.left, result); + preOrder(root.right, result); + } +} +// 中序遍历·递归·LC94_二叉树的中序遍历 +class Solution { + public List inorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + inorder(root, res); + return res; + } + + void inorder(TreeNode root, List list) { + if (root == null) { + return; + } + inorder(root.left, list); + list.add(root.val); // 注意这一句 + inorder(root.right, list); + } +} +// 后序遍历·递归·LC145_二叉树的后序遍历 +class Solution { + public List postorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + postorder(root, res); + return res; + } + + void postorder(TreeNode root, List list) { + if (root == null) { + return; + } + postorder(root.left, list); + postorder(root.right, list); + list.add(root.val); // 注意这一句 + } +} +``` Python: diff --git a/problems/前序/代码风格.md b/problems/前序/代码风格.md index 2be1ac36..e4378b4e 100644 --- a/problems/前序/代码风格.md +++ b/problems/前序/代码风格.md @@ -9,15 +9,17 @@ -------------------------- -# 看了这么多代码,谈一谈代码风格! +# 看了这么多代码,谈一谈代码风格! -其实在交流群里经常能看到大家发出来的代码,可以看出很多录友对代码规范应该不甚了解,代码看起来并不舒服。 +最近看了很多录友在[leetcode-master](https://mp.weixin.qq.com/s/wZRTrA9Rbvgq1yEkSw4vfQ)上提交的代码,发现很多录友的代码其实并不规范,这一点平时在交流群和知识星球里也能看出来。 + +很多录友对代码规范应该不甚了解,代码看起来并不舒服。 所以呢,我给大家讲一讲代码规范,我主要以C++代码为例。 需要强调一下,代码规范并不是仅仅是让代码看着舒服,这是一个很重要的习惯。 -# 题外话 +## 题外话 工作之后,**特别是在大厂,看谁的技术牛不牛逼,不用看谁写出多牛逼的代码,就代码风格扫一眼,立刻就能看出来是正规军还是野生程序员**。 @@ -25,15 +27,15 @@ 现在一些小公司,甚至大公司里的某些技术团队也不注重代码规范,赶进度撸出功能就完事,这种情况就要分两方面看: -* 第一种情况:这个项目在业务上赚到钱了,每年年终好几十万,那项目前期还关心啥代码风格,赶进度把功能撸出来,赚钱就完事了,例如15年的王者荣耀。 +* 第一种情况:这个项目在业务上具有巨大潜力,需要抢占市场,只要先站住市场就能赚到钱,每年年终好几十万,那项目前期还关心啥代码风格,赶进度把功能撸出来,赚钱就完事了,例如12年的微信,15年的王者荣耀。这些项目都是后期在不断优化的。 * 第二种情况:这个项目没赚到钱,半死不活的,代码还没有设计也没有规范,这样对技术人员的伤害就非常大了。 **而不注重代码风格的团队,99.99%都是第二种情况**,如果你赶上了第一种情况,那就恭喜你了,本文下面的内容可以不用看了,哈哈。 -# 代码规范 +## 代码规范 -## 变量命名 +### 变量命名 这里我简单说一说规范问题。 @@ -67,7 +69,7 @@ ![编程风格](https://img-blog.csdnimg.cn/20201119173039835.png) -## 水平留白(代码空格) +### 水平留白(代码空格) 经常看到有的同学的代码都堆在一起,看起来都费劲,或者是有的间隔有空格,有的没有空格,很不统一,有的同学甚至为了让代码精简,把所有空格都省略掉了。 @@ -89,7 +91,7 @@ int i, j; for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) ``` -花括号和函数保持同一行,并有一个空格例如: +大括号和函数保持同一行,并有一个空格例如: ``` while (n) { @@ -123,9 +125,13 @@ public: }; ``` -当然我并不是说一定要按照Google的规范来,代码风格其实统一就行,没有严格的说谁对谁错。 +这里关于大括号是否要重启一行? -# 总结 +Google规范是 大括号和 控制语句保持同一行的,我个人也很认可这种写法,因为可以缩短代码的行数,特别是项目中代码行数很多的情况下,这种写法是可以提高阅读代码的效率。 + +当然我并不是说一定要按照Google的规范来,**代码风格其实统一就行,没有严格的说谁对谁错**。 + +## 总结 如果还是学生,使用C++的话,可以按照题解中我的代码风格来,还是比较标准的。 diff --git a/problems/数组理论基础.md b/problems/数组理论基础.md index f061088f..a2c86ac4 100644 --- a/problems/数组理论基础.md +++ b/problems/数组理论基础.md @@ -118,21 +118,6 @@ public static void test_arr() { 这里面试中数组相关的理论知识就介绍完了。 -后续我将介绍面试中数组相关的五道经典面试题目,敬请期待! - - -## 其他语言版本 - - -Java: - - -Python: - - -Go: - - ----------------------- diff --git a/problems/面试题02.07.链表相交.md b/problems/面试题02.07.链表相交.md new file mode 100644 index 00000000..97045ed2 --- /dev/null +++ b/problems/面试题02.07.链表相交.md @@ -0,0 +1,109 @@ + +

+ + + + +

+

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

+ +## 面试题 02.07. 链表相交 + +题目链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists-lcci/ + +给定两个(单向)链表,判定它们是否相交并返回交点。请注意相交的定义基于节点的引用,而不是基于节点的值。换句话说,如果一个链表的第k个节点与另一个链表的第j个节点是同一节点(引用完全相同),则这两个链表相交。 + +示例 1: + +输入:listA = [4,1,8,4,5], listB = [5,0,1,8,4,5] + +输出:Reference of the node with value = 8 + +输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。 + + +## 思路 + +本来很简洁明了的一道题,让题目描述搞的云里雾里的。 + +简单来说,就是求两个链表交点节点的**指针**。 这里同学们要注意,交点不是数值相等,而是指针相等。 + +为了方便举例,假设节点元素数值相等,则节点指针相等。 + +看如下两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点: + +![面试题02.07.链表相交_1](https://code-thinking.cdn.bcebos.com/pics/%E9%9D%A2%E8%AF%95%E9%A2%9802.07.%E9%93%BE%E8%A1%A8%E7%9B%B8%E4%BA%A4_1.png)v + +我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图: + +![面试题02.07.链表相交_2](https://code-thinking.cdn.bcebos.com/pics/%E9%9D%A2%E8%AF%95%E9%A2%9802.07.%E9%93%BE%E8%A1%A8%E7%9B%B8%E4%BA%A4_2.png) + +此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到焦点。 + +否则循环退出返回空指针。 + +C++代码如下: + +```C++ +class Solution { +public: + ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { + ListNode* curA = headA; + ListNode* curB = headB; + int lenA = 0, lenB = 0; + while (curA != NULL) { // 求链表A的长度 + lenA++; + curA = curA->next; + } + while (curB != NULL) { // 求链表B的长度 + lenB++; + curB = curB->next; + } + curA = headA; + curB = headB; + // 让curA为最长链表的头,lenA为其长度 + if (lenB > lenA) { + swap (lenA, lenB); + swap (curA, curB); + } + // 求长度差 + int gap = lenA - lenB; + // 让curA和curB在同一起点上(末尾位置对齐) + while (gap--) { + curA = curA->next; + } + // 遍历curA 和 curB,遇到相同则直接返回 + while (curA != NULL) { + if (curA == curB) { + return curA; + } + curA = curA->next; + curB = curB->next; + } + return NULL; + } +}; +``` + +* 时间复杂度:$O(n + m)$ +* 空间复杂度:$O(1)$ + +## 其他语言版本 + + +Java: + + +Python: + + +Go: + + + + +----------------------- +* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) +* B站视频:[代码随想录](https://space.bilibili.com/525438321) +* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) +