代码编织梦想

LeetCode 343 整数拆分

题目链接:343. 整数拆分 - 力扣(Leetcode)

 将整数拆分,使得拆分结果的乘积最大化,一开始碰到类似的题目,很难想象应该拆分为多少个数,容易想复杂。但其实对于一个数n,可以先考虑将其拆分为两个数,1+(n-1),2+(n-2),...,一共有n//2种拆分方式,对于每一种拆分方式下的两个数,有可能它们当前的乘积已经最大,也有可能这两个数还能继续拆分,使乘积更大。dp数组dp[i]表示拆分i能得到的最大乘积,因此第一个for循环遍历n,第二个for循环从1开始遍历,模拟将i拆分为1+(i-1), 2+(i-2),...,(i-1)+1等等,在从左往右遍历的过程中,比i小的dp值已经计算得到,因此在当前拆分的基础上,可以比较继续拆分i-j会不会获得更大的乘积。dp数组从n=2开始初始化,第一个for循环就从3开始遍历,包含n;第二个for循环从1到i(不包含i),进一步优化中,数学上将一个数拆分为多个近似相同的数时乘积最大,两个数拆分到它们之间相同或只差1时,可以认为已经列举完可能出现最大值的拆分情况,因此j的遍历可以只到i//2。

动态规划问题中最难的部分是思路,代码实现倒是很简单:

class Solution:
    def integerBreak(self, n: int) -> int:
        dp = [0] * (n + 1)
        dp[2] = 1
        for i in range(3, n + 1):
            for j in range(1, i):
                dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j))
        return dp[n]

LeetCode 96 不同的二叉搜索树 

题目链接:96. 不同的二叉搜索树 - 力扣(Leetcode)

 这道题也比较难想思路,需要对二叉搜索树有较为深刻的理解。

首先dp数组的含义是节点值从1到i互不相同的二叉搜索树的种数,和题目要求的从 1 到 n 互不相同的二叉搜索树种树是一致的。对于二叉搜索树,以不同的节点为根节点时,二叉树的构成是有固定限制的,比根节点小的值构成其左子树,大的值位于右子树,左右子树又能继续以根节点作为参考,构成不同的子树。左右子树的数值不同,但是数值的相对关系是类似的,比如1-3和2-4可以构成的二叉树种数是相同的,二叉搜索树的种数因此也就可以根据左右子树的节点数量获得各自的种数,左右的种数相乘得到以某个值为根节点时的二叉搜索树种数。

由于1到i中可以以i个节点作为根节点,不同根节点的二叉搜索树是不同的,因此计算dp[i]时,需要一层for循环累加以不同节点作为根时的结果:

class Solution:
    def numTrees(self, n: int) -> int:
        # dp数组含义:dp[i]为节点值从 1 到 i 互不相同的 二叉搜索树 的种数
        dp = [0 for _ in range(n+1)]  # dp[0]为空节点
        dp[0], dp[1] = 1, 1
        for i in range(2, n+1):
            for j in range(i):
                dp[i] += dp[j] * dp[i-j-1]
        return dp[n]

小结

这两题的实现上还有一点点相似,都是两层for循环,时间复杂度是O(n^2),空间复杂度是O(n)。但dp数组的含义不同,第二层遍历的递推过程就有比较大的差异,一个是只取遍历过程中的最大值,一个需要不断累加。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/tsy_0827/article/details/129791169

算法刷题打卡009 | 栈应用题3道-爱代码爱编程

今天复习利用栈与队列的特性解决问题。 LeetCode 20. 有效的括号 题目链接:20. 有效的括号 - 力扣(Leetcode) 有效的括号中,嵌套在最内层的右括号会与最近的左侧左括号匹配,根据栈后入先出的特性,可以把从左到右遍历到的左括号暂存到栈中,遇到右括号时检查栈顶是否与之匹配,栈为空或不匹配时整体括号无效,直接返回;括号

算法刷题打卡015 | 二叉树相关题目3道-爱代码爱编程

LeetCode 110 平衡二叉树 题目链接:110. 平衡二叉树 - 力扣(Leetcode) 平衡二叉树主要需要判断其所有子树是否都是高度差不大于1,第一次接触时不知道如何确定递归的返回值,后序遍历时既需要知道左右子树的高度,又需要知道左右子树各自是否平衡。讲解中比较巧妙的做法是返回一个固定值(比如高度不会为负数,可以使用-1)作为以该节点为根的

算法刷题打卡017 | 二叉树相关题目4道-爱代码爱编程

LeetCode 654 最大二叉树 题目链接:654. 最大二叉树 - 力扣(Leetcode) 这一题的思路和前一天的构建二叉树类似,只是将划分左右的规则由中序遍历结果改为区间的最大值,因为题目描述中明确nums中所有值不相同,因此可以用index找到每个区间最大值的下标: # Definition for a binary tree node.

算法刷题打卡019 | 二叉搜索树的插入删除-爱代码爱编程

LeetCode 235 二叉搜索树的最近公共祖先 题目链接:235. 二叉搜索树的最近公共祖先 - 力扣(Leetcode) 昨天做二叉树最近公共祖先时也一并用迭代法做完这道题,,递归法用任意一种遍历方式都可以,不再赘述。  # Definition for a binary tree node. # class TreeNode: # d

算法刷题打卡021 | 回溯-组合问题-爱代码爱编程

回溯理论基础 第二次看回溯算法了,回溯的基本思路也已经基本掌握,接下来就是需要多做题,将回溯思想贯彻落实!具体理论参考代码随想录 (programmercarl.com)。 回溯的应用场景往往没有其他解题途径,只能枚举,但直接嵌套循环枚举只适用于循环层数较少的场景,通过在递归中使用for循环,可以实现在树中不断往深层遍历,而回溯是为了让程序能够走向其他

算法刷题打卡022 | 回溯2-爱代码爱编程

LeetCode 216 组合总和III 题目链接:216. 组合总和 III - 力扣(Leetcode)  这道题和77 组合解题思路完全相同,只是收集结果时多加了和为n的限制。具体代码实现时一度出现重复使用数字的情况,debug发现是for循环中调用递归函数时,每次遍历起始数字start传入了start+1,导致下一层遍历的起始位置有出入(可以获

算法刷题打卡023 | 回溯3-爱代码爱编程

39 组合总和以及40 组合总和II已经在第21次打卡完成,看了讲解部分当作复习,不再赘述,今天的重点在于理解分割字符串的过程。 LeetCode 131 分割回文串 题目链接:131. 分割回文串 - 力扣(Leetcode) 分割型的回溯一直还不太能理解,主要问题是怎么表示切割线。“在处理组合问题的时候,递归参数需要传入startIndex,表示

算法刷题打卡027 | 贪心算法-爱代码爱编程

贪心算是一种常识性的思路,没有固定规律套路可以总结,有时会和动态规划混淆,反正记住一点,就是能从子问题的局部最优推导出全局最优解。 LeetCode 455 分发饼干 题目链接:455. 分发饼干 - 力扣(Leetcode) 自己做题时想到用小尺寸的饼干优先满足小胃口的孩子,在代码实现时先遍历饼干,再遍历孩子,但写着写着思路有些混乱和不确定,一直想

算法刷题打卡029 | 贪心算法3-爱代码爱编程

LeetCode 1005. K 次取反后最大化的数组和 题目链接:1005. K 次取反后最大化的数组和 - 力扣(Leetcode)  看到题目的第一反应以为数组范围都是非负数,于是可以根据k的奇偶性,k为偶数时只对同一个数反复取反,直接返回数组和,k为奇数时返回数组和与最小值的差值。但数据的数据范围包含负数,要最大化数组和,就需要优先将负数取反,

算法刷题打卡030 | 贪心算法4-爱代码爱编程

LeetCode 860.柠檬水找零 题目链接:860. 柠檬水找零 - 力扣(Leetcode) 这道找零问题其实很简单,贪心在于收到20的钞票时优先用一张10和一张5找零,没有10的情况下才用3张5,因为5可以给10和20找零,而10只能用于给20找零,并且这个判断逻辑的前提是按bills的顺序进行。用比较简单的if-

算法刷题打卡032 | 贪心算法6-终篇-爱代码爱编程

贪心最后一天! LeetCode 738 单调递增的数字 题目链接:738. 单调递增的数字 - 力扣(Leetcode) 这题很明显需要贪心地替换数字,从后往前遍历,遇到前一位数位比当前大的数位,就能将当前数位替换为9,并将前一个数位的数值减1,但自己实现时并没有想到,一旦替换了某一位为9,比它低的数位都应该替换为9,这样才能保证数尽可能大,还避免

算法刷题打卡002 | 有序数组的平方,长度最小子数组,螺旋矩阵ii-爱代码爱编程

977.有序数组的平方 题目链接:977. 有序数组的平方 - 力扣(Leetcode) 前几天刚好又做了一遍这道题,主要思路是找到排序数组的分割点,将数组分为正、负两个部分(0或者第一个正数),然后从分割点开始向两边进行双指针遍历,较小的数优先放入结果数组。 class Solution: def sortedSquares

算法刷题打卡012 | 二叉树基础遍历-爱代码爱编程

递归遍历 复习递归函数三要素:递归函数的参数和返回值、终止条件以及单层递归的逻辑。 前序遍历:144. 二叉树的前序遍历 - 力扣(Leetcode) # Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, ri

算法刷题打卡026 |回溯6-爱代码爱编程

首先跟随代码随想录进行了最近回溯刷题的总结,加深理解和记忆(我真的好像记性不是很好...)。回溯和递归相伴相生,在树的遍历中其实也经常涉及回溯,一开始我也很怵递归和回溯,不太习惯递归的思路,刷题下来已经形成了一些肌肉记忆,理解回溯的树形结构也很有帮助。记住,只能用回溯的题目,本质上也是在暴力枚举,只是一般的for循环没法实现,时间复杂度没有优化,最多能针对

算法刷题打卡033 | 动态规划1-爱代码爱编程

理论基础 动态规划的应用前提是有重叠子问题,通过子问题状态推导获得问题的最终解。第二次做动态规划的题了,希望这次能对动态规划有更透彻的理解。 LeetCode 509 斐波那契数 题目链接:509. 斐波那契数 - 力扣(Leetcode) 斐波那契数列是经典动态规划入门题,但其实根据题目描述进行模拟也可以解题: class Solution:

算法刷题打卡034 | 动态规划2-爱代码爱编程

LeetCode 62 不同路径 题目链接:62. 不同路径 - 力扣(Leetcode) 不同路径算是二维dp的简单题了, 根据题意可以知道到达每个网格的不同路径可以有两个来源,递推关系就是累加这两个方向的不同路径数量,就能得到到达当前网格的不同路径总数: class Solution: def uniquePaths(self, m: i

算法刷题打卡007 | 字符串相关题目5道-爱代码爱编程

LeetCode 344 反转字符串 题目链接:344. 反转字符串 - 力扣(Leetcode)  由于题目要求原地修改,因此必须使用双指针进行前后交换,实现反转字符串(列表形式): class Solution: def reverseString(self, s: List[str]) -> None: """

算法刷题打卡025 | 回溯5-爱代码爱编程

LeetCode 491 递增子序列 题目链接:491. 递增子序列 - 力扣(Leetcode) 乍一看和子集问题很像,但自己写还真不容易写出来,没有用used记录当前一层遍历已经使用过的元素时,结果集合会出现重复,也不能直接套用子集问题的去重方式。 class Solution: def __init__(self): s

算法刷题打卡024 | 回溯4-爱代码爱编程

LeetCode 93 复原IP地址 题目链接:93. 复原 IP 地址 - 力扣(Leetcode)  这一题和分割回文串是一个思路,只需要将判断字符串是否回文的条件换成判断字符串代表的数字是否在合适范围。前导零的判断代码应该还可以更精简一些,一开始只判断了大于0的数字是否含有前导零,忽略了0本身也可能包含多个零,这种IP也不是有效的。另外还要注意收

算法刷题打卡005 | 哈希表相关题目4道_哈希表算法机考题-爱代码爱编程

今天复习哈希表,Python中哈希表常用dict(各种defaultdict, OrderedDict等等),先看看哈希表基础内容。简单来说,哈希表又叫散列表,将键值对中的键映射到散列表中的一个位置,可以加快查找的速度。对应的映射函数称为哈希函数(散列函数),类似于数组中直接用index获取元素值,dict中代入key获得的哈希函数值就是index,可以O