文章目录
- 【LeetCode热题100】打卡第31天:买卖股票的最佳时机&二叉树中的最大路径和
- ⛅前言
- 买卖股票的最佳时机
- 🔒题目
- 🔑题解
- 二叉树中的最大路径和
- 🔒题目
- 🔑题解
【LeetCode热题100】打卡第31天:买卖股票的最佳时机&二叉树中的最大路径和
⛅前言
大家好,我是知识汲取者,欢迎来到我的LeetCode热题100刷题专栏!
精选 100 道力扣(LeetCode)上最热门的题目,适合初识算法与数据结构的新手和想要在短时间内高效提升的人,熟练掌握这 100 道题,你就已经具备了在代码世界通行的基本能力。在此专栏中,我们将会涵盖各种类型的算法题目,包括但不限于数组、链表、树、字典树、图、排序、搜索、动态规划等等,并会提供详细的解题思路以及Java代码实现。如果你也想刷题,不断提升自己,就请加入我们吧!QQ群号:827302436。我们共同监督打卡,一起学习,一起进步。
博客主页💖:知识汲取者的博客
LeetCode热题100专栏🚀:LeetCode热题100
Gitee地址📁:知识汲取者 (aghp) - Gitee.com
Github地址📁:Chinafrfq · GitHub
题目来源📢:LeetCode 热题 100 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台
PS:作者水平有限,如有错误或描述不当的地方,恳请及时告诉作者,作者将不胜感激
买卖股票的最佳时机
🔒题目
原题链接:121.买卖股票的最佳时机
🔑题解
-
解法一:暴力(超时)
class Solution { public int maxProfit(int[] prices) { int max = 0; for (int i = 0; i < prices.length; i++) { for (int j = i + 1; j < prices.length; j++) { int price = prices[j] - prices[i]; if (price > max) { max = price; } } } return max; } }
复杂度分析:
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度: O ( 1 ) O(1) O(1)
其中 n n n 为数组中元素的个数
代码优化:动态规划(时间优化)
前面我们使用最为简单的暴力枚举,可以发现有很多重复性的计算,每次我们都需要去循环遍历重复计算当前元素左侧最大的一个元素,这显然是没有必要的,我们可以使用使用 一个 dp 数组来记录当前元素右侧最小的元素,我们从前往后遍历,①比当前元素小要么是上一个元素左侧最小值,②要么左侧没有比当前元素小的,那么当前的dp应该设置为当前元素,所以最终的动态转移方程就可以是
dp[i]=Math.min(dp[i-1],prices[i])
class Solution { public int maxProfit(int[] prices) { int[] dp = new int[prices.length]; // 这里需要初始化第一个元素,从第二个元素开始进行状态转移计算 dp[0] = prices[0]; for (int i = 1; i < prices.length; i++) { dp[i] = Math.min(dp[i - 1], prices[i]); } int max = 0; for (int i = 0; i < prices.length; i++) { max = Math.max(max, prices[i]-dp[i]); } return max; } }
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)
其中 n n n 为数组中元素的个数
代码优化:利用一个变量来记录左侧最小值(时间和空间优化)
上面我们使用动态规划,利用一个 dp 数组来记录当前元素左侧最小值,我们可以惊奇的发现在使用 dp 时,我们并没有用到 dp[i] 右侧的元素,我们可以完全省掉这个数组,改用一个变量来记录当前元素左侧最小值,同时也只需要一次循环,在从左往右遍历的过程中就能够实现左侧最小值的更新,以及当前值的计算,这样既节省了内存消耗,又节省了时间消耗,从而大大提高代码的性能
class Solution { public int maxProfit(int[] prices) { int leftMin = prices[0]; int max = 0; for (int i = 0; i < prices.length; i++) { leftMin = Math.min(leftMin, prices[i]); max = Math.max(max, prices[i] - leftMin); } return max; } }
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
其中 n n n 为数组中元素的个数
二叉树中的最大路径和
🔒题目
原题链接:124.二叉树中的最大路径和
🔑题解
-
解法一:递归
题解大致思路:首先我们需要理解题目的意思,先是理解什么是路径,其实就是 一笔画能够得到路径的最大值
上面这棵树的最大路径和,就是7+11+4+8+13=48
以3为根节点,组成的二叉树只有3,此时这棵树所有节点能够组合起来的最大值就是3,同理可以知道4、和6的最大路径分别是它们本身;现在以2为根节点,组成的二叉树就是 2,3,4,此时这颗树组和起来的最大值就是 9,同理可以计算出 -8的最大路径是-2,而1的最大路径是 2、4、1,也就是7,因为1的右子树小于0,所以最大路径的组合不需要右子树,左子树我们要选用左节点左右子树中较大路径,左节点,也就是
Math.max(cur.left.left, cur.left.left)
通过上面对题目的解析,其实我们可以总结出一个计算公式,也就是当前节点能够形成的最大路径是
cur.val+Math.max(cur.left, 0)+Math.max(cur.right, 0)
,这个公式的意思就是当前节点cur
能够形成的最大路径的组成是当前值cur.val
还有左子树构成路径的最大值,如果左子树的最大值小于0,则直接舍弃,还有右子树构成路径的最大值,同理小于0直接舍弃。但是对于class Solution { private int maxPathSum = Integer.MIN_VALUE; public int maxPathSum(TreeNode root) { calculateMaxPathSum(root); return maxPathSum; } private int calculateMaxPathSum(TreeNode root) { if (root == null) { return 0; } // 计算左子树的最大路径和 int leftMax = Math.max(calculateMaxPathSum(root.left), 0); // 计算右子树的最大路径和 int rightMax = Math.max(calculateMaxPathSum(root.right), 0); // 当前节点能够构成的最大路径和 int curMax = root.val + Math.max(leftMax, 0) + Math.max(rightMax, 0); // 更新目前最大路径和 maxPathSum = Math.max(maxPathSum, curMax); // 返回子节点下左右子树中较大的路径和 return root.val + Math.max(leftMax, rightMax); } }
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n),需要访问每一个节点
- 空间复杂度: O ( n ) O(n) O(n),递归的次数取决于树的高度,最好情况(树是一颗完全平衡二叉树) l o g n logn logn,最坏情况(树是单链表) n n n
其中 n n n 为数组中元素的个数