- 博客(42)
- 收藏
- 关注
原创 打卡第四十九天:接雨水、柱状图中最大的矩形
题目文章视频本题暴力解法也是也是使用双指针。首先要明确,要按照行来计算,还是按照列来计算。按照行来计算如图: 按照列来计算如图: 首先,如果按照列来计算的话,宽度一定是1了,我们再把每一列的雨水的高度求出来就可以了。可以看出每一列雨水的高度,取决于,该列 左侧最高的柱子和右侧最高的柱子中最矮的那个柱子的高度。例如求列4的雨水高度,如图:列4 左侧最高的柱子是列3,高度为2(以下用lHeight表示)。列4 右侧最高的柱子是列7,高度为3(以下用rHeight表示)。列4 柱子的高度为1(以下用height表
2024-08-20 21:17:00
740
原创 打卡第四十八天:每日温度、下一个更大元素I、下一个更大元素II
题目说如果不存在对应位置就输出 -1 ,所以result数组如果某位置没有被赋值,那么就应该是是-1,所以就初始化为-1。在遍历nums2的过程中,我们要判断nums2[i]是否在nums1中出现过,因为最后是要根据nums1元素的下标来更新result数组。
2024-08-18 22:03:51
983
原创 打卡第四十六天:回文子串、最长回文子序列
题目文章视频本题如果我们定义,dp[i] 为 下标i结尾的字符串有 dp[i]个回文串的话,我们会发现很难找到递归关系。dp[i] 和 dp[i-1] ,dp[i + 1] 看上去都没啥关系。要看回文串的性质。 如图:判断字符串S是否是回文,如果知道 s[1],s[2],s[3] 这个子串是回文的,那么只需要比较 s[0]和s[4]这两个元素是否相同,如果相同的话,这个字符串s 就是回文串。那么判断一个子字符串(字符串下标范围[i,j])是否回文,依赖于,子字符串(下标范围[i + 1, j - 1]))
2024-08-17 10:43:55
866
原创 打卡第四十五天:不同的子序列、两个字符串的删除操作、编辑距离
那最后当然是取最小值,所以当word1[i - 1] 与 word2[j - 1]不相同的时候,递推公式:dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1});同理dp[0][j] = j;因为 dp[i][j - 1] + 1 = dp[i - 1][j - 1] + 2,所以递推公式可简化为:dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
2024-08-16 21:32:55
724
原创 打卡第四十四天:最长公共子序列、不相交的线、最大子序和、判断子序列
而是dp[6]。在回顾一下dp[i]的定义:包括下标i之前的最大连续子序列和为dp[i]。最大的连续子序列,就应该找每一个i为终点的连续最大子序列。所以在递推公式的时候,可以直接选出最大的dp[i]。
2024-08-15 22:02:24
1015
原创 打卡第四十三天:最长递增子序列、最长连续递增序列、最长重复子数组
举个例子A[0]如果和B[0]相同的话,dp[1][1] = dp[0][0] + 1,只有dp[0][0]初始为0,正好符合递推公式逐步累加起来。但dp[i][0] 和dp[0][j]要初始值,因为 为了方便递归公式dp[i][j] = dp[i - 1][j - 1] + 1;即当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;根据dp[i][j]的定义,dp[i][j]的状态只能由dp[i - 1][j - 1]推导出来。
2024-08-14 20:56:31
689
原创 打卡第四十二天:买卖股票的最佳时机IV、最佳买卖股票时机含冷冻期、买卖股票的最佳时机含手续费
如果i为1,第1天买入股票,那么递归公式中需要计算 dp[i - 1][1] - prices[i] ,即 dp[0][1] - prices[1],那么大家感受一下 dp[0][1] (即第0天的状态二)应该初始成多少,只能初始为0。那么dp[i][0] = max(dp[i - 1][0], dp[i - 1][3] - prices[i], dp[i - 1][1] - prices[i]);今天卖出了股票(状态三),同上分析,dp[0][2]初始化为0,dp[0][3]也初始为0。
2024-08-13 21:56:30
875
原创 打卡第四十一天:买卖股票的最佳时机
同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);那么dp[i][0]应该选所得现金最大的,所以dp[i][0] = max(dp[i - 1][0], -prices[i]);一定是选最大的,所以 dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);dp[i][1]选 dp[i-1][0] - prices[i],还是dp[i - 1][1]
2024-08-12 21:34:13
995
原创 打卡第三十九天:打家劫舍I、打家劫舍II、打家劫舍III
题目文章视频当前房屋偷与不偷取决于 前一个房屋和前两个房屋是否被偷了。dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。决定dp[i]的因素就是第i房间偷还是不偷。如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i] ,即:第i-1房一定是不考虑的,找出 下标i-2(包括i-2)以内的房屋,最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。如果不偷第i房间,那么dp[i] = dp[i - 1],即考 虑i-1房,(注意这里是考虑,并不是一定要偷i-1
2024-08-10 13:41:48
605
原创 打卡第三十八天:零钱兑换、完全平方数、单词拆分
凑足总额为j - coins[i]的最少个数为dp[j - coins[i]],那么只需要加上一个钱币coins[i]即dp[j - coins[i]] + 1就是dp[j](考虑coins[i])所以dp[j] 要取所有 dp[j - coins[i]] + 1 中最小的。从递推公式中可以看出,dp[i] 的状态依靠 dp[j]是否为true,那么dp[0]就是递推的根基,dp[0]一定要为true,否则递推下去后面都都是false了。中可以看出每次dp[j]都要选最小的,
2024-08-09 21:22:11
658
原创 打卡第三十七天:完全背包、零钱兑换II、组合总和IV、爬楼梯(进阶)
dp[i]有几种来源,dp[i - 1],dp[i - 2],dp[i - 3] 等等,即:dp[i - j]。因为只要得到nums[j],排列个数dp[i - nums[j]],就是dp[i]的一部分。既然递归公式是 dp[i] += dp[i - j],那么dp[0] 一定为1,dp[0]是递归中一切数值的基础所在,如果dp[0]是0的话,其他数值都是0了。因为递推公式dp[i] += dp[i - nums[j]]的缘故,dp[0]要初始化为1,这样递归其他dp[i]的时候才会有数值基础。
2024-08-08 21:32:40
617
原创 打卡第三十六天:最后一块石头的重量II、目标和、一和零
题目文章视频本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。本题物品的重量为stones[i],物品的价值也为stones[i]。对应着01背包里的物品重量weight[i]和 物品价值value[i]。确定dp数组以及下标的含义dp[j]表示容量为j的背包,最多可以背最大重量为dp[j]。01背包中,dp[j]的含义,容量为j的背包,最多可以装的价值为 dp[j]。
2024-08-07 21:43:18
679
原创 打卡第三十五天:背包理论基础、分割等和子集
首先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0。如图:看其他情况。状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
2024-08-06 21:15:01
759
原创 打卡第三十四天:不同路径、不同路径II、整数拆分、不同的二叉搜索树
从递归公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1] 中可以看出,一定是从左到右一层一层遍历,这样保证推导dp[i][j]的时候,dp[i - 1][j] 和 dp[i][j - 1]一定是有数值。这里要看一下递推公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1],dp[i][j]都是从其上方和左方推导而来,那么从左到右一层一层遍历就可以了。所以递推公式:dp[i] += dp[j - 1] * dp[i - j];
2024-08-05 21:31:57
518
原创 打卡第三十二天:动态规划基础、斐波那契数、爬楼梯、使用最小花费爬楼梯
动态规划,Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的,例如:有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i]。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
2024-08-04 21:10:46
984
原创 打卡第三十一天:合并区间、单调递增的数字、监控二叉树
举个例子,数字:332,从前向后遍历的话,那么就把变成了329,此时2又小于了第一位的3了,真正的结果应该是299。那么空节点不能是无覆盖的状态,这样叶子节点就要放摄像头了,空节点也不能是有摄像头的状态,这样叶子节点的父节点就没有必要放摄像头了,而是可以把摄像头放在叶子节点的爷爷节点上。例如:98,一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]--,然后strNum[i]给为9,这样这个整数就是89,即小于98的最大的单调递增整数。
2024-08-02 21:21:47
734
原创 打卡第三十天:用最少数量的箭引爆气球、无重叠区间、划分字母区间
此时问题就是要求非交叉区间的最大个数。如图:区间,1,2,3,4,5,6都按照右边界排好序。取 区间1 和 区间2 右边界的最小值,因为这个最小值之前的部分一定是 区间1 和区间2 的重合部分,如果这个最小值也触达到区间3,那么说明 区间 1,2,3都是重合的。接下来就是找大于区间1结束位置的区间,是从区间4开始。区间4结束之后,再找到区间6,所以一共记录非交叉区间的个数是三个。总共区间个数为6,减去非交叉区间的个数3。移除区间的最小数量就是3。
2024-08-01 21:52:16
658
原创 打卡第二十九天:加油站、分发糖果、柠檬水找零、根据身高重建队列
整个插入过程如下:排序完的people: [[7,0], [7,1], [6,1], [5,0], [5,2],[4,4]]
2024-08-01 20:50:33
1016
原创 打卡第二十八天:买卖股票的最佳时机II、跳跃游戏、跳跃游戏II、K次取反后最大化的数组和
i 每次移动只能在 cover 的范围内移动,每移动一个元素,cover 得到该元素数值(新的覆盖范围)的补充,让 i 继续移动下去。而 cover 每次只取 max(该元素数值补充后的范围, cover 本身范围)。如果 cover 大于等于了终点下标,直接 return true 就可以了。
2024-07-30 22:15:31
587
原创 打卡第二十七天:贪心算法理论基础、分发饼干、摆动序列、最大子序和
从代码角度上来讲:遍历 nums,从头开始用 count 累积,如果 count 一旦加上 nums[i]变为负数,那么就应该从 nums[i+1]开始从 0 累积 count 了,因为已经变为负数的 count,只会拖累总和。很容易可以发现,对于我们当前考虑的这个数,要么是作为山峰(即 nums[i] > nums[i-1]),要么是作为山谷(即 nums[i] < nums[i - 1])。注意版本一的代码中,可以看出来,是先遍历的胃口,在遍历的饼干,那么可不可以 先遍历 饼干,在遍历胃口呢?
2024-07-29 21:19:33
895
原创 打卡第二十五天:递增子序列、全排列、全排列II、重新安排行程、N皇后、解数独
程序运行的时候对unordered_set 频繁的insert,unordered_set需要做哈希映射(也就是把key通过hash function映射为唯一的哈希值)相对费时间,而且每次重新定义set,insert的时候其底层的符号表也要做相应的扩充,也是费事的。拿题目中的示例为例,输入: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]] ,这是有4个航班,那么只要找出一种行程,行程里的机场个数是5就可以了。
2024-07-27 18:22:19
710
原创 打卡第二十四天:复原IP地址、子集、子集II
startIndex一定是需要的,因为不能重复分割,记录下一层递归分割的起始位置。从图中可以看出,同一树层上重复取2 就要过滤掉,同一树枝上就可以重复取2,因为同一树枝上元素的集合才是唯一子集!其实子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和 子集{2,1}是一样的。求排列问题的时候,就要从0开始,因为集合是有序的,{1, 2} 和{2, 1}是两个集合。如果要是全排列的话,每次要从0开始遍历,为了跳过已入栈的元素,需要使用used。不会无限递归,因为每次递归的下一层就是从i+1开始的。
2024-07-27 15:08:26
813
原创 打卡第二十三天:组合总和、组合总和II、分割回文串
切割问题可以抽象为组合问题如何模拟那些切割线切割问题中递归如何终止在递归循环中如何截取子串如何判断回文总结出来难究竟难在哪里也是一种需要锻炼的能力。关于模拟切割线,其实就是index是上一层已经确定了的分割线,i是这一层试图寻找的新分割线除了这些难点,本题还有细节,例如:切割过的地方不能重复切割所以递归函数需要传入i + 1。
2024-07-27 14:49:09
771
原创 打卡第二十二天:回溯算法基础、组合、组合总和III、电话号码的字母组合
例如 k = 2,n = 4的话,就是在集合[1,2,3,4,5,6,7,8,9]中求 k(个数) = 2, n(和) = 4的组合。然后收集结果,结束本层递归。图中可以看出遍历的深度,就是输入"23"的长度,而叶子节点就是我们要收集的结果,输出["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]。第一次取1,集合变为2,3,4 ,因为k为2,我们只需要再取一个数就可以了,分别取2,3,4,得到集合[1,2] [1,3] [1,4],以此类推。
2024-07-24 21:30:02
610
原创 打卡第二十一天:修剪二叉搜索树、将有序数组转换二叉搜索树、把二叉搜索树转换为累加树
题目文章视频1、确定参数和返回值有返回值可以通过递归函数的返回值来移除节点。2、确定终止条件修剪的操作并不是在终止条件上进行的,所以就是遇到空节点返回就可以了。3、确定单层递归的逻辑如果root(当前节点)的元素小于low的数值,那么应该递归右子树,并返回右子树符合条件的头结点。如果root(当前节点)的元素大于high的,那么应该递归左子树,并返回左子树符合条件的头结点。接下来要将下一层处理完左子树的结果赋给root->left,处理完右子树的结果赋给root->right。精简迭代因为二
2024-07-23 21:52:44
786
原创 打卡第二十天:二叉搜索树的最近公共祖先、二叉搜索树中的插入操作、删除二叉搜索树中的结点
从根节点搜索,第一次遇到 cur节点是数值在[q, p]区间中,即 节点5,此时可以说明 q 和 p 一定分别存在于 节点 5的左子树,和右子树中。将删除节点(元素7)的左孩子放到删除节点(元素7)的右子树的最左面节点(元素8)的左孩子上,就是把5为根节点的子树移到了8的左孩子的位置。动画中的二叉搜索树中,删除元素7, 那么删除节点(元素7)的左孩子就是5,删除节点(元素7)的右子树的最左面节点是元素8。终止条件就是找到遍历的节点为null的时候,就是要插入节点的位置了,并把插入的节点返回。
2024-07-22 20:59:51
722
原创 打卡第十八天:二叉搜索树的最小绝对差、二叉搜索树中的众数、二叉树的最近公共祖先
求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从底向上的遍历方式。在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断。要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。
2024-07-20 16:05:10
1013
原创 打卡第十七天:最大二叉树、合并二叉树、二叉搜索树中的搜索、验证二叉搜索树
最终t1就是合并之后的根节点。因为是传入了两个树,那么就有两个树遍历的节点t1 和 t2,如果t1 == NULL 了,两个树合并就应该是 t2 了(如果t2也为NULL也无所谓,合并之后就是NULL)。要定义一个longlong的全局变量,用来比较遍历的节点是否有序,因为后台测试数据中有int最小值,所以定义为longlong的类型,初始化为longlong最小值。输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了叶子节点了。
2024-07-20 14:52:46
951
原创 打卡第十六天:平衡二叉树、二叉树的所有路径、左叶子之和、完全二叉树的结点个数、找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树
要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。节点4 的path,在遍历到节点3,path+3,遍历节点3的递归结束之后,返回节点4(回溯的过程),path并不会把3加上。因为题目中其实没有说清楚左叶子究竟是什么节点,那么左叶子的明确定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点。判断一个树的左叶子节点之和,那么一定要传入树的根节点,递归函数的返回值为数值之和,所以为int使用题目中给出的函数就可以了。
2024-07-19 22:56:45
772
原创 打卡第十五天:翻转二叉树、对称二叉树、二叉树的最大深度、二叉树的最小深度
确定递归函数的参数和返回值:因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。那么使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个最小距离 也同样是最小深度。左右孩子都为空的节点才是叶子节点。本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
2024-07-14 22:39:40
1024
原创 打卡第十四天:二叉树的层序遍历
层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是,就放进result数组中,随后返回result就可以了。层序遍历一个二叉树。相对于 104.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了。这道题目说是二叉树,但116题目说是完整二叉树,其实没有任何差别,一样的代码一样的逻辑。在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度。层序遍历,取每一层的最大值。
2024-07-14 17:03:51
310
原创 打卡第十一天:逆波兰表达式求值、滑动窗口最大值、前K个高频元素
为什么不用快排呢, 使用快排要将map转换为vector的结构,然后对整个数组进行排序, 而这种场景下,我们其实只需要维护k个有序的序列就可以了,所以使用优先级队列是最优的。暴力法是O(n×k)的算法。此时我们需要一个队列,这个队列呢,放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,每次移动之后,队列告诉我们里面的最大值是什么。每次窗口移动的时候,调用que.pop(滑动窗口中移除元素的数值),que.push(滑动窗口添加元素的数值),然后que.front()就返回我们要的最大值。
2024-07-13 15:47:16
660
原创 打卡第十天:栈与队列、用栈实现队列、用队列实现栈、有效的括号、删除字符串中所有相邻重复项
队列是先进先出,栈是先进后出。栈提供push 和 pop 等等接口,所有元素必须符合先进后出规则,所以栈不提供走访功能,也不提供迭代器(iterator)。不像是set 或者map 提供迭代器iterator来遍历所有元素。所以STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器)。栈的内部结构,栈的底层实现可以是vector,deque,list 都是可以的, 主要就是数组和链表的底层实现。deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。
2024-07-12 21:49:02
857
原创 打卡第九天:反转字符串里的单词、右旋转字符串、实现strStr()、重复的子字符串
KMP的主要思想是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。next数组就是一个前缀表(prefix table)。前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。例子:要在文本串:aabaabaafa 中查找是否出现过一个模式串:aabaaf。文本串中第六个字符b 和 模式串的第六个字符f,不匹配了。如果暴力匹配,发现不匹配,此时就要从头匹配了。
2024-07-12 01:34:02
620
原创 打卡第八天:反转字符串、反转字符串II、替换数字
先扩充数组到替换后应有的大小,然后在字符串新结尾和旧结尾分别放上i、j指针。将j从后往前替换字符直到到头。从后向前的好处是,不用申请新数组且避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。注意for循环语句内容。
2024-07-10 18:12:27
107
原创 打卡第七天:四数相加II、赎金信、三数之和、四数之和
而用和前一个元素比较的判断时,当a走到下标为2的-1,也有{-1, -1, 2}的组合,但是是重复的,这样就把这个重复的排除了。思路:两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3)。a不变,b是控制变大的元素,c是控制变小的元素。直到遍历结束,i++。
2024-07-09 21:29:47
653
原创 打卡第六天:哈希表、有效的字母异位词、两个数组的交集、快乐数、两数之和
哈希函数,把学生的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道这位同学是否在这所学校里了。哈希碰撞:映射到同一位置。解决方法:拉链法和线性探测法。拉链法:发生冲突的元素都被存储在链表中。线性探测法:一定要保证tableSize大于dataSize。常见的三种哈希结构:数组、set(集合)、map(映射)红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。
2024-07-08 21:53:59
803
原创 打卡第四天:两两交换链表中的结点、删除链表的倒数第N个结点、链表相交、环形链表II
因此快的速度是慢的2倍,时间一致,快的距离就是慢的距离的2倍,有2(x+y)=x+y+n×(y+z)。利用一快一慢两个指针(快指针一次移动2结点、慢指针一次移动1结点)同时移动,如果相遇则有环,且一定在环中相遇,且一定在慢指针绕环的第一圈时就相遇。双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。步骤三 cur->next->next此时是虚拟头指向2号再指向1号,此步骤把1号出去的指针指向3号,即前面预设的tmp2。
2024-07-06 20:27:17
439
原创 打卡第三天:移除链表元素、设计链表、反转链表
太大的数组占用空间,删除插入效率低下。链表是链式存储结构,动态管理,资源重复利用。但需要分配另外的空间存储前驱和后继所在位置。
2024-07-06 01:29:18
315
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人