代码随想录
文章平均质量分 90
非社会人士
这个作者很懒,什么都没留下…
展开
-
代码随想录 | Day 60(完结) - LeetCode 84. 柱状图中最大的矩形
第0位的左边界(不包含在内)应该是-1,末尾位的右边界(不包含在内)应该是“末尾 + 1”,所以indSmallerLeft[0]和indSmallerRight[height.size() - 1]分别初始化为-1和height.size()。相比接雨水,主要是从“找左、右两边各自最高的柱子”变成了“要找左、右两边各自第一个比当前柱子小的位置”,使其分别作为左、右边界(不包含在内),再令当前柱子的高作为矩形的高,两者相乘得到当前位置的矩形大小。今天是单调栈的最后一天,延续了昨天的接雨水问题。原创 2022-11-20 00:00:23 · 386 阅读 · 0 评论 -
代码随想录 | Day 59 - LeetCode 503. 下一个更大元素II、LeetCode 42. 接雨水
这个高度取决于当前位置左边部分的最高柱子、和右边部分最高柱子,两者中较小值,再减去当前位置的柱子高度,就是当前位置的雨水高度。在遍历之前,可以将最高柱子高度初始化为当前柱子高度,这样一来,如果左边或右边没有更高的柱子,最后当前位置的雨水量就是0。第3种情况的实现较为复杂,需要首先得到中间柱子,然后使其出栈,再得到左柱子,然后再计算宽度、高度,加雨水量。是三种方法中最难的一种,核心思想是维持单调递减的栈,在出现非递减元素时,弹出栈中元素的同时加雨水,每次所加的雨水数量都是其宽度和高度的乘积。原创 2022-11-19 17:14:54 · 717 阅读 · 0 评论 -
代码随想录 | Day 58 - LeetCode 739. 每日温度、LeetCode 496. 下一个更大元素 I
不符合的话则需要弹出所有比当前数值小的元素,并同时对弹出元素对应位置的结果赋值(结果就是当前元素下标与被弹出元素下标的差值),最后。为实现这一目的,理论上既需要知道每个元素的值,又需要知道其下标,似乎需要将两者都放入栈中。这样的做法会使得右边没有更大数字的元素永远无法进行res的赋值,而题目要求这些元素的结果均设置为0,所以可以在初始化时就将所有结果设置为0。判断当前元素是否在map中存在,既可以像上面一样使用map.find(),也可以通过判断map.count()的结果是否为0来判断。原创 2022-11-18 20:58:22 · 709 阅读 · 0 评论 -
代码随想录 | Day 57 - LeetCode 647. 回文子串、LeetCode 516. 最长回文子序列
前两种情况的dp取值都不依赖于其他dp值,而第3种情况的dp[i][j]则依赖于dp[i + 1][j - 1],也就是其左下角位置。所以需要初始化dp矩阵的对角线(对应情况1)以及对角线上每个值右边的第1个位置(对应情况2),这样才能保证在情况3下,每个位置的左下角被填充。)相比上一题,将“连续的子串”改为了“非连续的子序列”,问题也从“回文子串的数量”变成了“最长回文子序列的长度”。因为问题的转变,所以定义方面,这道题的dp[i][j](i ≥ j)变成了“s[i : j]的最长回文子串长度”。原创 2022-11-17 18:04:34 · 520 阅读 · 0 评论 -
代码随想录 | Day 56 - LeetCode 583. 两个字符串的删除操作、LeetCode 72. 编辑距离
对于第1种可能,由于dp[i - 1][j - 1] + 1要么与dp[i - 1][j]相等,要么与dp[i][j - 1]相等(因为删除次数+1对应多删1个元素,即多一个元素),所以dp[i - 1][j - 1] + 2等于dp[i - 1][j] + 1或dp[i][j - 1] + 1。两者相等时,相对于word1[0 : i - 2]和word1[0 : j - 2]不需要额外的删除次数,所以删除次数仍然是dp[i - 1][j - 1];另外这两道题的初始化部分,不再是全部初始化为0了。原创 2022-11-17 00:20:28 · 302 阅读 · 0 评论 -
代码随想录 | Day 55 - LeetCode 392. 判断子序列、LeetCode 115. 不同的子序列
第0行,对应s为空,dp[0][j](j > 0)对应空字符“删除任意字符后出现t[0 : i - 1]”的个数,空字符无论怎么删除都还是空字符,所以有0中删除方式,dp[0][j](j > 0)都应该初始化为0。)需要首先将问题转化为“求s和t的最大相同子序列长度”,用dp[i][j]表示“s[0 : i - 1]和t[0 : j - 1]的最大相同子序列长度”(使用i - 1、j - 1而不用i、j的原因与之前一样,还是为了后面的初始化方便)。不断的右移t指针,使t指针所指的值匹配s指针所指的值。原创 2022-11-16 02:48:08 · 200 阅读 · 0 评论 -
代码随想录 | Day 53 - LeetCode 1143. 最长公共子序列、LeetCode 1035. 不相交的线、LeetCode 53. 最大子序和
对于text1[i - 1]与text2[j - 1]相等的情况,最长公共子序列长度相比没有text1[i - 1]和text2[j - 1]时就要增加一位,所以dp[i][j] = dp[i - 1][j - 1] + 1;),子序列不再要求是连续的,剩余地方都与其一样。实现中需要注意,因为为了初始化的方便而采用了额外多一行、一列的dp矩阵,所以与dp[i][j]对应的是text1[i - 1]和text2[j - 1]是否想等的判断,而不是text1[i]和text2[j]。原创 2022-11-16 00:57:35 · 315 阅读 · 0 评论 -
代码随想录 | Day 52 - LeetCode 300. 最长递增子序列、LeetCode 674. 最长连续递增序列、LeetCode 718. 最长重复子数组
所以要计算dp[i]的话,就要从0遍历到(i - 1)位置,遇到比nums[i]小的数字nums[j],就将dp[j]加上1,然后取所有dp[j] + 1(j < i)当中的最大值作为dp[i]的结果。这一题要用到二维dp数组,dp[i][j]定义为“对于nums1的[0, i]部分,nums2的[0, j]部分,两者分别以nums1[i]和nums2[j]结尾的最长重复子数组长度”。当nums1[i]与nums2[j]相等时,这一长度就在dp[i - 1][j - 1]的基础上再增加1;原创 2022-11-15 00:03:51 · 253 阅读 · 0 评论 -
代码随想录 | Day 51 - LeetCode 309.最佳买卖股票时机含冷冻期、LeetCode 714.买卖股票的最佳时机含手续费
这道题的重点仍然是状态的定义和划分,状态整体上仍然分为“持有”和“不持有”两大类。其中“不持有”又分为“当天卖出”、“当天是冷冻期”、和“当天已经过了冷冻期”这3种状态,加上“持有”共计4种状态。将“持有”、“当天卖出”、“当天是冷冻期”、和“当天已经过了冷冻期”分别设为状态0~3。所以要做出的相应改变就是,从“持有”状态向“不持有”状态转移时,要减掉手续费,也就是将原本的dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] +主要是第1题的冷冻期问题比较复杂。原创 2022-11-14 22:14:16 · 321 阅读 · 0 评论 -
代码随想录 | Day 50 - LeetCode 123. 买卖股票的最佳时机III、LeetCode 188. 买卖股票的最佳时机IV
第0天就处于状态1的话,只可能买入第0天的股票,所以dp[0][1] = -prices[0];虽然第0天无法处于状态2,但一方面可以看作是第0天买入后又立即卖出,另一方面卖出时的利润都是不低于0,设置为0可以保证后面的比较大小顺利进行,所以dp[0][2] = 0;由于最多可以买卖两次,所以原本“持有”和“不持有”这两种状态不再够用,需要扩展为“未曾买卖”、“买过第1次”、“卖过第1次”、“买过第2次”、“买过第2次”这5种状态,第i天每个状态下能获得的最大利润对应dp[i][0]~dp[i][4]。原创 2022-11-14 21:07:10 · 312 阅读 · 0 评论 -
代码随想录 | Day 49 - LeetCode 121. 买卖股票的最佳时机、LeetCode 122.买卖股票的最佳时机II
所以“第i天持有”的第2种情况,也就是“之前未买入,第i天买入”需要改为“第(i - 1)天未持有,第i天买入”。所以状态转移方程就可以总结为dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])和dp[i][1] = max(dp[i - 1][1], -prices[i])。dp数组的第i天有两个数值,dp[i][0]表示第i天“不持有”股票对应的最大所得现金,dp[i][1]表示第i天“持有”股票对应的最大所得现金。原创 2022-11-13 18:06:34 · 344 阅读 · 0 评论 -
41.动态规划(9) | 打家劫舍、打家劫舍II(h)、打家劫舍III(h)
而如果打劫的话,就意味着上一个房屋不在考虑范围内,所以要取上“上一个房屋对应的dp值”与“当前房屋价值”的和,即dp[i - 2] + nums[i]。而如果打劫当前节点,那么其左右子节点都不能被打劫,只能选择各自dp的第0位,再加上当前节点的val,对应cur->val + dpLeft[0] + dpRight[0]。如果打劫当前节点,那么当前点的左右子节点就都不能被打劫了。所以应该考虑当前节点的孙子子树,也就是当前节点的“左子节点的左右子节点、右子节点的左右子节点”,这4个节点作为根节点的4棵树。原创 2022-11-07 22:00:50 · 343 阅读 · 0 评论 -
40.动态规划(8) | 单词拆分(h)、多重背包
代码中需要注意,递归的循环中,i一定是从indBegin开始的。自己起初是设置为从indBegin + 1开始的,那么下一层递归的indBegin参数就不再应该是(i + 1)了,而应该是i,否则会错误地略过当前层的第(indBegin + 1)个元素。该问题有多种解法,其中比较容易理解和实现的是转化为01背包问题,也就是把每个数量为n的物品拆分为n个数量为1的物品,然后用01背包的解法解决。,当前的目标字符串,也就是当前背包容量,对应的物品是“当前目标字符串的所有后缀”,而所有物品就是。原创 2022-11-07 15:46:33 · 257 阅读 · 0 评论 -
39.动态规划(7) | 爬楼梯、零钱兑换、完全平方数(h)
第1题(LeetCode 70. 爬楼梯 (进阶))是day 38中的第2题,当时用了斐波那契数列的解法。也可以将其看作完全背包问题,背包容量对应总台阶数,物品对应每次能上的台阶数。比如在该问题中物品数共2件,第一件物品的重量和价值都是1,第二件物品的重量和价值都是2。具体的背包问题跟day 44中的第3题(LeetCode 377. 组合总和 Ⅳ)一样,是求排列数量。所以按照dp[j] += dp[j - weight[i]]的状态转移方程,dp[0] = 1作为初始化,外层循环遍历背包、内层循环遍历物品原创 2022-11-05 20:23:44 · 551 阅读 · 0 评论 -
38.动态规划(6) | 完全背包(h)、零钱兑换 II、组合总和 Ⅳ(h)
dp[i][j]的定义是与01背包问题一样的。所以用dp[i][j]表示“有前i件物品,装满容量j的方法数”的话,状态转移方程就变成了dp[i][j] = dp[i - 1][j] + dp[i][j - weight[i]]。所以状态转移公式中的max()需要改为min(),同时将dp[0]以外的DP数值初始化为一个“很大的值”,dp[0]还是初始化为0。所以最终的状态转移方程是dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])。原创 2022-11-05 00:47:02 · 185 阅读 · 0 评论 -
37.动态规划(5) | 最后一块石头的重量 II(h)、目标和(h)、一和零(h)
多了当前的第i件物品后,会新增将当前第i件物品装进去的若干装法,为保证空间大小j需减小为(j - weight[i]),所以这部分装法数量对应dp[i - 1][j - weight[i]]。那么问题就转化为了“背包维度0容量为m,背包维度1容量为n,每个字符串对应一件物品,每个物品的价值都为1,维度0的重量为该字符串中字符'0'个数,维度1的重量为该字符串中字符'1'个数,这种情况下的01背包问题”。而对于这两个集合,只要求得其中一个的和,用“整体和”减去它,便能得到另一个的和了。两者之差尽可能地小。原创 2022-11-04 01:16:29 · 119 阅读 · 0 评论 -
36.动态规划(4) | 01背包(h)、分割等和子集(h)
另外在矩阵版本的双重循环中,由于(j - weights[i])可能会小于0,所以在其小于0时,直接选择上方的值dp[i - 1][j]。又因为遍历方向是从右向左,所以j是在递减的,那么(j - weights[i])就也是在递减。所以,只需要将内层循环进行到j - weights[i] < 0为止就可以了,所以内层循环条件可以设置为j - weights[i] >= 0,即j >= weights[i]。而对于容量,只能从0开始初始化,所以dp[i][0]对应背包最大容量为0,而不是最大容量为1的情况。原创 2022-11-02 23:56:46 · 191 阅读 · 0 评论 -
35.动态规划(3) | 整数拆分(h)、不同的二叉搜索树(h)
如果左子数有j个节点,那么右子树就有(i - 1 - j)个节点,j可以从1取到(i - 1),所以总共有(i - 1)种情况。这其中j和(i - 1 - j)都一定是小于i的,所以其左子树的类型就有dp[j]种,右子树的类型就有dp[i - 1 - j]种,两者相乘就得到了“左子树有j个节点情况下,整个树可能的结构数”。对于数字i,它总是会被拆分成2个或2个以上的数字。所以这道题的状态转移方程是dp[i] = max(j * (i - j), j * dp[j]),其中j需要从1遍历到(i - 1)。原创 2022-10-31 19:47:06 · 122 阅读 · 0 评论 -
34.动态规划(2) | 不同路径、不同路径 II
初始化方面,由于第0行的所有点都是只能由出发点向右走的,第0列所有点都是只能由出发点向下走的,所以第0行和第0列的值都应该是1。题解中还给出一种数论的解法。如果惯性思维将其初始化为1,那对于“第0行的第0个元素就是障碍物,但其他行的第0列元素并没有障碍物”的情况,由于是从第1行开始遍历的,那就会错误地将其他行的第0列元素设置为1。如果使用搜索的方法,那么搜索树的深度就是m + n - 1(深度按从1开始计算),二叉树的节点数就是2^(m+n-1)了,对应时间复杂度O( 2^(m+n-1) ),所以会超时。原创 2022-10-31 00:54:50 · 121 阅读 · 0 评论 -
33.动态规划(1) | 斐波那契数、爬楼梯、使用最小花费爬楼梯
所以已知了第i - 1阶和第i - 2阶楼梯的最小花费后,再令两者各自加上自己前进所需的花费,即cost[i - 1]和cost[i - 2],就得到了2种路线和对应的花费。从两者中取较小的一个当做第i阶的花费即可,所以dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]。因为每次只能上1阶或2阶,所以对于i阶楼梯,它的上一步要么是第i - 1阶,要么就是第i - 2阶。原创 2022-10-30 22:47:54 · 154 阅读 · 0 评论 -
32.贪心(6) | 单调递增的数字(h)、买卖股票的最佳时机含手续费(h)、监控二叉树(h)
第1题(LeetCode 738. 单调递增的数字)自己的思路跟题解比较相近,但有缺陷,且最终没有实现成功。贪心策略是让不符合要求的右半部分数字都变为9,且同时左半部分也要改变,使得变化后的数字要小于原数字。所以关键问题是找到上述左右部分的分割点。 具体做法是首先将数字转化为对应的字符串。因为题目要求从左至右要满足非递减关系,所以如果从左向右遍历的话,如果为了满足当前数字小于右边数字的要求而让当前数字减1,那么当前数字左边的数字,或许又不满足要求了。所以应该从右向左遍历,只要遇到当前数字大于右原创 2022-10-30 19:51:52 · 100 阅读 · 0 评论 -
31.贪心(5) | 无重叠区间、划分字母区间(h)、合并区间
排序后,将首个区间的左边界设置为begin,右边界设置为end,然后开始遍历剩余区间,贪心目标是让当前区间尽可能多地与其他区间合并。所以,需要预设一个最短段下标,将其设置为第一个字母出现的最大下标,并在遍历时不断根据新字母出现的最大下标,将其更新为更大的值。只要遍历到该最短段下标,就说明段内的所有字母都出现在当前段内,不会出现在之后,于是就可以将当前遍历过的部分当作一段,并开始下一段的遍历。在遍历时,遇到区间的左边界大于或等于重叠区间右边界时,就对不重复区间数+1,并更新重叠区间右边界为当前区间的右边界。原创 2022-10-29 22:43:58 · 99 阅读 · 0 评论 -
30.贪心(4) | 柠檬水找零、根据身高重建队列(h)、用最少数量的箭引爆气球(h)
此外,可以用一个新的vector来保存结果,当要插入某个元素(对应下标i)时,因为结果中保存的元素身高都不小于该元素,其目标插入位置就是people[i][1],不需再用一个循环来确定。比如输入为[[7,0],[5,0],[7,2],[6,1],[5,2],[7,1]]时,上面错误代码的输出就会是[[7,0],[5,0],[7,1],[6,1],[7,2],[5,2]],而正确答案是[[5,0],[7,0],[5,2],[6,1],[7,1],[7,2]]。函数,需要再次熟悉下。原创 2022-10-29 19:31:19 · 349 阅读 · 0 评论 -
29.贪心(3) | K次取反后最大化的数组和、加油站(h)、分发糖果
首先对于第0个点,由于sumCur是第0~i个点的汽油剩余量的和,相当于模拟了从第0个点出发的过程,而到第i个点时汽油剩余量却变成了负数,说明从0个点出发不成功。而对于从第1个点出发的选择,因为第0个点的汽油剩余量一定是非负数(否则在第0个点时,sumCur就已经被更新为0了),所以从第1个点出发的sumCur一定是小于或等于从第0个点出发的sumCur的。那么既然从第0个点出发的sumCur都已经是负数,从第1个点出发的sumCur就也一定是相等或更小的负数了,所以从第1个点出发也是不行的。原创 2022-10-29 13:44:29 · 316 阅读 · 0 评论 -
28.贪心(2) | 买卖股票的最佳时机II、跳跃游戏(h)、跳跃游戏II
所以可以探索下下标1~3数字的覆盖范围,于是得到下标1数字2的覆盖范围是下标2~3,下标2数字1的覆盖范围是下标3,下标3数字0的覆盖范围为空。而对于数组[2, 3, 1, 1, 4],下标0数字2的覆盖范围是下标1~2。具体来说,如果设当前下标为0,当前最多能跳2步,对应的下两个数字是[5, 1],那么当前跳1步的话,下一步的最大跳跃下标是1 + 5 = 6;这一题同样有DP解法,但DP在之后的章节会专门练习,其中也会有这一题,所以这里和之后的贪心算法题目中不再研究DP解法,在之后的DP章节中再研究。原创 2022-10-28 22:49:17 · 1079 阅读 · 0 评论 -
27.贪心(1) | 分发饼干、摆动序列、最大子序和(h)
反之,如果上一个转折点是山峰,峰点数字被保存为pre,那么遇到比pre小的数字也不能说明就是山谷,只能说明比山峰小,它之后可能紧跟更小的数字。而对于一些在山腰的数字,虽然在最优解下是被认定为山腰的,但这并不妨碍它可以被认定为山峰/山谷,只不过得到的不是最优解,最坏情况下,它被认定为山峰/山谷后得到的答案是1。而初始化方面,因为题目说一个数字也是1个摆动序列,所以对应的最小值为1,所以就需将dp的第0行2个数字都初始化为1,并在遍历nums时,每次都将nums[i]的个数字也都初始化为1。原创 2022-10-28 18:19:06 · 298 阅读 · 0 评论 -
26.回溯(6) | 重新安排行程(h)、N皇后、解数独(h)
第1题(332. 重新安排行程)自己艰难地AC。这道题用DFS更合适些,但还没刷到那部分,就先练习下回溯解法。按照自己的思路,首先对备选车票按照目的地的字典序排序。然后向存放结果的vector,res中添加"JFK",再用day 29中排列问题的解法对车票进行循环遍历和递归。当当前元素对应的used表为true,或当前车票不是上一张车票的后继时跳过。 方法比较简单,但经历了多次失败。起先是因为将递归函数出口像之前的题目一样直接写成了:而其他地方也没有任何处理。如果写成这样,递归函数会返回原创 2022-10-24 00:07:09 · 344 阅读 · 0 评论 -
25.回溯(5) | 递增子序列(h)、全排列、全排列 II(h)
但本题中,由于数组中有重复元素,如果用上一题的方法则无法区分当前树枝上两个或多个数值相同的数字,所以应该将bool数组的大小调整为与备选数组一致,这样才能与备选数组中的数字一一对应。这是因为排列不同于组合,在树层中遍历的起始下标是0,而原本的i > begin也就替换为i > 0,这就导致如果上一层被选取的元素恰好与当前层第1个待选元素相等的话,当前层的元素无法被选取。一样,题目中可能有相等的数字,但要求结果不能有重复的序列。而题目又要求结果的排列不能重复,所以需要同一数层的去重,这里的去重针对的是。原创 2022-10-19 23:31:31 · 149 阅读 · 0 评论 -
24.回溯(4) | 复原IP地址(h)、子集、子集II
里面最简便的第3种实现方式,即添加每个重复区间的第一个元素并递归后,对当前重复区间的其他元素continue。类似,在已知上一个分割点的情况下,在递归函数中循环遍历下一个可能的分割点。所有节点恰好满足不重复且是子集的要求,所以在实现时也只需要套用模板,但需要把res.push_back(path)从递归出口移到函数开始处,让每次递归,即每个节点都能运行这一句,使每个节点都被放入到结果当中。而自己的写法是不论取或不取,都进行递归,相比前者会对不取元素的情况进行处理,所以像这种取子集的题就很适合。原创 2022-10-18 22:49:12 · 160 阅读 · 0 评论 -
23.回溯(3) | 组合总和、组合总和II(h)、分割回文串(h)
所以在DP过程中,每个点的true或false取值取决于其左下角的点,那么在计算第i行之前,要首先得到第i + 1行的计算结果,就需要在行方向上倒序遍历了。同时它也表示每个元素在当前层的已使用情况,与在树枝上的含义相反,如果值为false,说明在当前层已经被使用过,不能再使用与其值相等的元素。而每行从斜对角线开始的前2个元素,都是没有对应的左下角点值的,所以需要进行特殊的判断。这样的方法也可行但对于指针的处理比较麻烦,很容易出错,所以还是建议使用题解的方案,或者下面的简化版本(题解中的第二种解法)。原创 2022-10-18 17:38:07 · 122 阅读 · 0 评论 -
22.回溯(2) | 组合总和III、电话号码的字母组合
第1题(216. 组合总和III)相比day 24中的模板只是对组合中数字的和限定为了n,所以在递归出口处判断和是否为n即可。记录和的具体方法可以像之前day 18中第2题(112. 路径总和)一样转化为减法,判断最终结果是否为0。这道题按上面的写法虽然也能AC,但还可以优化,重点在于剪枝,横向和纵向都可进行剪枝:其中,横向上的剪枝是我自己没有考虑到的。 第2题(17. 电话号码的字母组合)需要首先把每个数字按键和对应的多个字母记录整理下来,并同之前一样设置2个全局变量path和res。原创 2022-10-17 22:25:10 · 233 阅读 · 0 评论 -
21.回溯(1) | 77. 组合
自己之前在其他题目中还实现过另外一种回溯方法,将上面的“在每次回溯中横向遍历所有下一个数字的可能”变为了“将每个数字都分为‘取’和‘不取’这2种可能”。在自己的编译器中用全局变量cnt统计了n和k分别取20和16时,递归函数的运行次数,发现该方法的cnt约是第一种方法cnt的2倍左右。分析原因,发现这种方法在不取某个值时,也会进行递归,而第一种方法则不会,效率更高。主要内容是回溯的理论基础和基本代码模板,需要注意一定要将一直保持不变的vector设置为全局变量,否则有可能超时。原创 2022-10-17 20:48:11 · 159 阅读 · 0 评论 -
20.树(9) | 修剪二叉搜索树(h)、将有序数组转换为二叉搜索树、把二叉搜索树转换为累加树
自己AC的递归方法是将数组范围作为递归参数,根据坐标取数组的中间数作为中间节点的val,再用数组的左半部分递归建立左子树,用数组的右半部分递归建立右子树。)自己实现的递归解法会出现奇奇怪怪的报错“ERROR: AddressSanitizer: heap-use-after-free on address”,测试用例是一颗普普通通的树[2, 1, 3],low = 3,high = 5。题解的递归解法也是反中序遍历,只是当前节点的val虽同样采用全局变量,但用来保存前继节点val的值。原创 2022-10-16 17:10:22 · 151 阅读 · 0 评论 -
19.树(8) | 二叉搜索树的最近公共祖先、二叉搜索树中的插入操作、删除二叉搜索树中的节点(h)
不是上面2种情况的话,再判断当前节点与p、q的val的大小关系,如果都比当前节点小,就递归向左查找,反之向右查找。删除节点的函数返回删除后,需要调整部分的树的根节点。这样一来,对于非叶子节点的这个赋值是等于没有操作的,只有对叶子节点,才会插入新建的节点。)的递归法自己AC的思路是判断当前节点是否可以插入,插入条件为应该插入左边且左节点为空,或者应该插入右节点且右节点为空。迭代法沿用使用parent记录父节点的思路,不断向下查找,直到cur为空节点,parent为叶子节点,再对parent插入。原创 2022-10-15 22:12:31 · 79 阅读 · 0 评论 -
18.树(7) | 二叉搜索树的最小绝对差、二叉搜索树中的众数、二叉树的最近公共祖先(h)
递归法自己的起初的思路是绝对值最小值一定存在于中间节点与左节点的差,和中间节点与右节点的差,这两者之间。二叉树遍历的递归解法在题解中是以“当前节点为空”作为递归出口,但我自己经常将“当前节点为叶子节点”作为递归出口,这样做会使遍历掠过所有叶子节点,如果一定要这样写,就要在递归出口处也加上对当前叶子节点的处理。代码中需要pre节点来保存上一个节点,因为中序遍历只有在中间部分(左节点递归后,右节点递归前)才是中间节点的处理部分,也是每个节点的处理部分,所以在这里更新pre。这里也因上面的第2点发现一个。原创 2022-10-15 02:14:58 · 172 阅读 · 0 评论 -
17.树(6) | 最大二叉树、合并二叉树、二叉搜索树中的搜索、验证二叉搜索树(h)
题解使用pre用于保存中序遍历中当前节点的前一节点,将其val与当前节点的val进行比较进行判断,如果cur为第一个节点,那么可以根据pre为空而跳过这一次比较,避免了测试用例的值可能是INT_MIN而导致的错误。如果只有一个节点有左/右子节点,就将该左/右子节点赋给左节点的左/右子节点。)自己的思路是递归查看当前节点是否满足val > 左子节点的val,且val < 右子节点的val,满足的话则递归判断左,右子节点是否满足,都满足的情况下才返回true,否则返回false。原创 2022-10-14 20:51:48 · 157 阅读 · 0 评论 -
16.树(5) | 找树左下角的值(h)、路径总和、路径总和 II、从中序与后序遍历序列构造二叉树、从前序与中序遍历序列构造二叉树
中间首先设立递归出口,自己的实现使用左闭右闭的区间表示方法,所以出口即为中序(或后序)遍历的左下标与右下标相等,此时说明该节点是叶子节点,对其赋值并return即可。题解的递归解法则用了前序遍历(中、后序也可以,因为都是左节点优先,符合题目要求),在遍历到每个节点时都取当前深度,然后每遇到深度更大的节点来更新结果为当前节点值。迭代解法因为要在遍历过程中保留路径信息,所以在栈中除了要存储节点,sum外,还需存储一个用于保存当前节点路径的vector,实现较为麻烦,没有必要,所以没有实现。原创 2022-10-13 21:05:51 · 192 阅读 · 0 评论 -
15.树(4) | 平衡二叉树(h)、257. 二叉树的所有路径、404.左叶子之和
自己的思路是用后序遍历求当前节点的最大深度(即树的高度),首先判断左、右两子节点是不是平衡二叉树,如果不是,那么当前树也不是;)递归方法方面,自己想到的是对每个节点根据其是左节点还是右节点做标记,再根据其是左还是右节点左不同处理,只有在当前是左节点且为叶子节点时才返回当前节点值,否则就返回当前节点的左节点的递归,与右节点的递归的和。在有了递归法题解思路的基础上,迭代法就比较容易了,使用前中后和层序遍历都可以,只需要在遍历途中,将满足递归法中判断左叶子节点所用条件的节点值加起来,就能得到所有左叶子节点和。原创 2022-10-10 00:30:12 · 281 阅读 · 0 评论 -
14.树(3) | 二叉树的最大深度、n叉树的最大深度、二叉树的最小深度、完全二叉树的节点个数
通用方法的递归和迭代解法时间复杂度都是O(n),而该针对完全二叉树的特定解法的递归次数是logn,且每次递归中计算左、右子树的最左、最右节点深度,这一步骤时间复杂度也是O(logn),所以总的算法时间复杂度是O(logn * logn)。前序遍历的递归解法则需要注意一点,用children的size()作为循环的次数的话,如果size()为0,即没有子节点,那么循环就不会进行,其中的递归也不会进行,不会像上一题 (上的节点数,那么当前节点的最小高度不是1,而是(1+另一非空节点的高度),这一点是与第1题(原创 2022-10-08 02:23:54 · 478 阅读 · 0 评论 -
13.树(2) | 二叉树的层序遍历、翻转二叉树II、对称二叉树
中序遍历首先对左子节点进行反转,再将左右子节点交换,注意此时新的左子节点是旧的未经过反转的右子节点,新的右子节点才是旧的已经反转好的左子节点,所以最后一步应该对新的左子节点进行反转操作。迭代法让树的左子树,右子树分别按“中->左->右”的“先序”和“中->右->左”的“先序”将其元素放入队列中,每取出一对,就进行比较,并将其各自的左右/右左节点放入队列中,直到某次比较不相等就返回false,或队列为空就返回true。另外,该实现方法中的队列也可换用栈(递归能实现的,栈也能实现),其他地方不变。原创 2022-10-07 22:40:51 · 241 阅读 · 0 评论