- 博客(54)
- 收藏
- 关注
原创 代码随想录算法训练营 单调栈part02
当前高度大于栈顶高度,出栈,计算出当前墙和栈顶的墙之间水的多少,然后计算当前的高度和新栈的高度的关系,重复第 2 步。每次移动到数组中的一个新的位置 i,就将当前单调栈中所有对应值小于 nums[i]的下标弹出单调栈,这些值的下一个更大元素即为 nums[i]。只遍历一次序列是不够的,例如序列 [2,3,1],最后单调栈中将剩余 [3,1],其中元素 [1] 的下一个更大元素还是不知道的。单调栈中保存的是下标,从栈底到栈顶的下标在数组 nums 中对应的值是单调不升的。一、下一个更大元素II。
2023-09-23 11:14:47
193
原创 代码随想录算法训练营 单调栈part01
具体地,每次我们移动到数组中一个新的位置 i,就将当前单调栈中所有小于 nums2[i] 的元素弹出单调栈,当前位置右边的第一个更大的元素即为栈顶元素,如果栈为空则说明当前位置右边没有更大的元素。我们可以先预处理 nums2,使查询 nums1 中的每个元素在 nums2中对应位置的右边的第一个更大的元素值时不需要再遍历 nums2。倒序遍历 nums2,并用单调栈中维护当前位置右边的更大的元素列表,从栈底到栈顶的元素是单调递减的。第 2 个子问题:如何存储第 1 个子问题的结果。二、下一个更大元素 I。
2023-09-23 11:07:16
209
原创 代码随想录算法训练营 动态规划part17
如果 s[i]=s[j],则首先得到 s 的下标范围 [i+1,j−1] 内的最长回文子序列,然后在该子序列的首尾分别添加 s[i] 和 s[j],即可得到 s 的下标范围 [i,j] 内的最长回文子序列,因此 dp[i][j]=dp[i+1][j−1]+2;如果 s[i]≠s[j],则 s[i] 和 s[j] 不可能同时作为同一个回文子序列的首尾,因此 dp[i][j]=max(dp[i+1][j],dp[i][j−1])。最终得到 dp[0][n−1] 即为字符串 s 的最长回文子序列的长度。
2023-09-22 20:22:22
288
原创 代码随想录算法训练营 动态规划part15
让 s[i] 参与匹配,这时候只需要让 s 中 [0,i−1]个字符去匹配 t 中的 [0,j−1]字符即可,同时满足 s[i]=t[j]。此时匹配值为 f[i−1][j−1]不让 s[i] 参与匹配,也就是需要让 s 中 [0,i−1]个字符去匹配 t 中的 [0,j] 字符。此时匹配值为 f[i−1][j]定义 f[i][j] 为考虑 s 中 [0,i] 个字符,t 中 [0,j] 个字符的匹配个数。最终 f[i][j]f就是两者之和。
2023-09-22 20:06:53
287
原创 代码随想录算法训练营 动态规划part14
为何定义最大和 dp[i] 中必须包含元素 nums[i] :保证 dp[i] 递推到 dp[i+1] 的正确性;由于 dp[i] 只与 dp[i−1] 和 nums[i] 有关系,因此可以将原数组 nums 用作 dp 列表,即直接在 nums 上修改即可。: dp[0]=nums[0],即以 nums[0] 结尾的连续子数组最大和为 nums[0]。: 设动态规划列表 dp ,dp[i] 代表以元素 nums[i] 为结尾的连续子数组最大和。返回 dp列表中的最大值,代表全局最大值。
2023-09-22 19:58:26
286
原创 代码随想录算法训练营 动态规划part13
实现方式为遍历 j 时,每轮执行 dp[i]=max(dp[i],dp[j]+1)。当 nums[i]>nums[j] 时: nums[i] 可以接在 nums[j] 之后(此题要求严格递增),此情况下最长上升子序列长度为 dp[j]+1;当 nums[i]<=nums[j] 时: nums[i] 无法接在 nums[j] 之后,此情况上升子序列不成立,跳过。转移方程: dp[i] = max(dp[i], dp[j] + 1) for j in [0, i)。前几天算法课上老师讲了。
2023-09-22 19:48:12
247
原创 代码随想录算法训练营 动态规划part11
请选一个喜欢的吧/(ㄒoㄒ)/~~一、买卖股票的最佳时机III。一、买卖股票的最佳时机III。同上一题一样ε(┬┬﹏┬┬)3。二、买卖股票的最佳时机IV。
2023-09-22 19:29:45
373
原创 代码随想录算法训练营 动态规划part09
设数组 prices 的长度为 n ,由于只能先买入后卖出,因此第 1 天买可在未来 n−1 天卖出,第 2 天买可在未来 n−2 天卖出……以此类推,共有 (n−1)+(n−2)+⋯+0=n(n−1)/2 种情况,时间复杂度为 =O(N^2)。例如,若第 1 天价格低于第 2 天价格,即第 1 天成本更低,那么我们一定不会选择在第 2 天买入。更新前 i 天的最高利润 profit ,即选择「前 i−1 天最高利润 profit 」和「第 i 天卖出的最高利润 price - cost 」中的最大值。
2023-09-22 19:21:03
263
原创 代码随想录算法训练营 动态规划part07
即 f(n)为以上两种情况之和,即 f(n)=f(n−1)+f(n−2) ,以上递推性质为斐波那契数列。转移方程: dp[i+1]=dp[i]+dp[i−1],即对应数列定义 f(n+1)=f(n)+f(n−1)。当为 1 级台阶: 剩 n−1 个台阶,此情况共有 f(n−1) 种跳法。当为 2 级台阶: 剩 n−2 个台阶,此情况共有 f(n−2)种跳法。斐波那契数列问题: f(0)=0 , f(1)=1 , f(2)=1。青蛙跳台阶问题: f(0)=1 , f(1)=1 , f(2)=2。
2023-09-22 17:17:50
313
原创 代码随想录算法训练营 动态规划part06
代表当没有任何硬币的时候,存在凑成总和为 0 的方案数量为 1;当「状态定义」与「基本初始化」有了之后,我们不失一般性的考虑 f[i][j] 该如何转移。同时硬币相当于我们的物品,每种硬币可以选择「无限次」,很自然的想到「完全背包」。定义 f[i][j]为考虑前 iii 件物品,凑成总和为 jjj 的方案数量。因此我们有显而易见的初始化条件:f[0][0]=1,其余 f[0][x]=0。为了方便初始化,我们一般让 f[0][x] 代表不考虑任何物品的情况。emmmmm看官方题解吧。
2023-09-22 16:57:02
338
原创 代码随想录算法训练营 动态规划part05
将每次操作中「重量较少/相等」的石子放到「负号堆」,代表在这次操作中该石子重量在「最终运算结果」中应用 −-− 运算符。其实所谓的「有放回」操作,只是触发调整「某个原有石子」所在「哪个堆」中,并不会真正意义上的产生「新的石子重量」。当对“虚拟石子”添加 + 符号,即可 +(a−b),展开后为 a−b,即起始石子 a 和 b 所在「石子堆」不变。当对“虚拟石子”添加 − 符号,即可 −(a−b),展开后为 b−a,即起始石子 a 和 b 所在「石子堆」交换。一最后一块石头的重量 II。
2023-09-21 09:18:37
292
原创 代码随想录算法训练营 动态规划part03
假设 n 个节点存在二叉排序树的个数是 G (n),令 f(i) 为以 i 为根的二叉搜索树的个数,则。当 i 为根节点时,其左子树节点个数为 i-1 个,右子树节点为 n-i,则。综合两个公式可以得到 卡特兰数 公式。当然动态和贪心都可以。二、不同的二叉搜索树。
2023-09-21 08:58:29
460
原创 代码随想录算法训练营 动态规划part02
注意,对于第一行 dp[0][j],或者第一列 dp[i][0],由于都是在边界,所以只能为 1。优化:因为我们每次只需要 dp[i-1][j],dp[i][j-1],所以我们只要记录这两个数。动态方程:dp[i][j] = dp[i-1][j] + dp[i][j-1]我们令 dp[i][j] 是到达 i, j 最多路径。
2023-09-20 14:30:52
1516
原创 代码随想录算法训练营 动态规划part01
原理: 在递归法的基础上,新建一个长度为 nnn 的数组,用于在递归时存储 f(0) 至 f(n) 的数字值,重复遇到某数字则直接从数组取用,避免了重复的递归计算。原理: 把 f(n) 问题的计算拆分成 f(n−1) 和 f(n−2) 两个子问题的计算,并递归,以 f(0) 和 f(1) 为终止条件。转移方程: dp[i+1]=dp[i]+dp[i−1] ,即对应数列定义 f(n+1)=f(n)+f(n−1)。原理: 以斐波那契数列性质 f(n+1)=f(n)+f(n−1) 为转移方程。
2023-09-20 14:22:06
215
原创 代码随想录算法训练营 贪心算法 part06
注意到如果减小 str[i] 以后不满足 str[i−1]<=str[i],那么肯定有 str[i−1]==str[i],此时就需要再 str[i−1]−1,递归地会处理到某个位置 idx,我们发现 str[idx]==str[idx+1]==...=str[i]。然后只要str[idx]−1,然后后面都补上 9 即可。遍历各位数字的时候,求当前最大的数字 max。那么我们找到从高到低第一个满足 str[i]>str[i+1] 的位置,然后把 str[i]−1 ,再把后面的位置都变成 9 即可。
2023-09-20 14:07:48
76
原创 代码随想录算法训练营 贪心算法part05
假设在某一种最优的选择方法中,[lk,rk]是首个(即最左侧的)区间,那么它的左侧没有其它区间,右侧有若干个不重叠的区间。设想一下,如果此时存在一个区间 [lj,rj],使得 rj<rk ,即区间 j 的右端点在区间 k 的左侧,那么我们将区间 k 替换为区间 j,其与剩余右侧被选择的区间仍然是不重叠的。我们可以不断地寻找右端点在首个区间右端点左侧的新区间,将首个区间替换成该区间。由于我们选择的是首个区间,因此在左侧不会有其它的区间,那么左端点在何处是不重要的,我们只要任意选择一个右端点最小的区间即可。
2023-09-20 13:57:21
83
原创 代码随想录算法训练营 贪心算法part03
存在 0 值 或 剩余取反次数为偶数:直接返回当前取反数组的总和( 0 值可抵消任意次数的取反操作,将偶数次的取反操作应用在同一数值上,结果不变);最终,取以上 2轮遍历 left 和 right 对应学生糖果数的 最大值 ,这样则 同时满足左规则和右规则 ,即得到每个同学的最少糖果数量。因此,为了让取反后的结果尽可能的大,我们应当取反 −2∗x 尽可能大的数值。相邻的学生中,评分高的学生必须获得更多的糖果 等价于 所有学生满足左规则且满足右规则。经过此规则分配后,可以保证所有学生糖数量 满足左规则。
2023-09-19 21:59:48
80
原创 代码随想录算法训练营 贪心算法part02
对于连续上涨交易日: 设此上涨交易日股票价格分别为 p1,p2,...,pn ,则第一天买最后一天卖收益最大,即 pn−p1;等价于每天都买卖,即 pn−p1=(p2−p1)+(p3−p2)+...+(pn−p(n−1))。遍历整个股票交易日价格列表 price,并执行贪心策略:所有上涨交易日都买卖(赚到所有利润),所有下降交易日都不买卖(永不亏钱)。设 tmp 为第 i-1 日买入与第 i 日卖出赚取的利润,即 tmp = prices[i] - prices[i - 1];最后的步数就是最少步数。
2023-09-19 21:48:44
91
原创 代码随想录算法训练营 回溯算法part06
回溯是递归的副产品,只要有递归就会有回溯,所以回溯法也经常和二叉树遍历,深度优先搜索混在一起,因为这两种方式都是用了递归。回溯法就是暴力搜索,并不是什么高效的算法,最多再剪枝一下。组合问题:N个数里面按一定规则找出k个数的集合排列问题:N个数按一定规则全排列,有几种排列方式切割问题:一个字符串按一定规则有几种切割方式子集问题:一个N个数的集合里有多少符合条件的子集棋盘问题:N皇后,解数独等等void backtracking(参数) {if (终止条件) {存放结果;
2023-09-18 21:34:42
116
原创 代码随想录算法训练营 回溯算法part05
即通过元素交换,先固定第 1位元素( n种情况)、再固定第 2 位元素( n−1种情况)、... 、最后固定第 n 位元素( 1种情况)。终止条件: 当 x = len(nums) - 1 时,代表所有位已固定(最后一位只有 1 种情况),则将当前组合 nums 转化为数组并加入 res ,并返回。固定元素: 将元素 nums[i] 和 nums[x] 交换,即固定 nums[i] 为当前位元素。固定元素: 将元素 nums[i] 和 nums[x] 交换,即固定 nums[i] 为当前位元素。
2023-09-18 21:30:27
104
原创 代码随想录算法训练营 回溯算法part03
这意味着在某轮选择中,若当前元素与其左边元素相等,则说明它已经被选择过,因此直接跳过当前元素。使用一个路径变量 path 搜索,path 全局使用一个(注意结算的时候,要生成一个拷贝),因此在递归执行方法结束以后需要回溯,即将递归之前添加进来的元素拿出去;在叶子结点是空字符串的时候结算,此时 从根结点到叶子结点的路径,就是结果集里的一个结果,使用深度优先遍历,记录下所有可能的结果。因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!
2023-09-18 21:12:10
146
原创 代码随想录算法训练营 回溯算法part02
首先需要一个字符串s来收集叶子节点的结果,然后用一个字符串数组result保存起来,index是记录遍历第几个数字了,就是用来遍历digits的(题目中给出数字字符串),同时index也表示树的深度。如果此时path里收集到的元素和(sum) 和targetSum(就是题目描述的n)相同了,就用result收集当前的结果。单层遍历逻辑:首先要取index指向的数字,并找到对应的字符集(手机键盘的字符集)。本题就是在[1,2,3,4,5,6,7,8,9]这个集合中找到和为n的k个数的组合。
2023-09-18 20:49:54
99
原创 代码随想录算法训练营 回溯算法 part01
回溯法解决的问题都可以抽象为树形结构,因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,都构成的树的深度。再来看一下参数,因为回溯算法需要的参数可不像二叉树递归的时候那么容易一次性确定下来,所以一般是先写逻辑,然后需要什么参数,就填什么参数。什么时候达到了终止条件,树中就可以看出,一般来说搜到叶子节点了,也就找到了满足条件的一条答案,把这个答案存放起来,并结束本层递归。回溯法也可以叫做回溯搜索法,它是一种搜索的方式。回溯是递归的副产品,只要有递归就会有回溯。
2023-08-28 15:10:20
65
原创 代码随想录训练营 二叉树 part7
当 left和 right同时不为空 :说明 p,q 分列在 root 的 异侧 (分别在 左 / 右子树),因此 root 为最近公共祖先,返回 root;当 left 为空 ,right 不为空 :p,q都不在 root 的左子树中,直接返回 right。从底至顶回溯,当节点 p,q在节点 root 的异侧时,节点 root 即为最近公共祖先,则向上返回 root。p 和 q在 root 的子树中,且分列 root 的 异侧(即分别在左、右子树中);
2023-08-15 20:43:19
102
1
原创 代码随想录算法训练营 二叉树part6
因为是传入了两个树,那么就有两个树遍历的节点t1 和 t2,如果t1 == NULL 了,两个树合并就应该是 t2 了(如果t2也为NULL也无所谓,合并之后就是NULL)。题目中说了输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了叶子节点了。寻找一个不符合条件的节点,如果没有找到这个节点就遍历了整个树,如果找到不符合的节点了,立刻返回。参数传入的是存放元素的数组,返回该数组构造的二叉树的头结点,返回类型是指向节点的指针。
2023-08-14 19:58:38
85
原创 代码随想录算法训练营 二叉树part5
就是以 后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。切割点在后序数组的最后一个元素,就是用这个元素来切割中序数组的,所以必要先切割中序数组。中序数组我们都切成了左中序数组和右中序数组了,那么后序数组就可以按照左中序数组的大小来切割,切成左后序数组和右后序数组。后序数组没有明确的切割元素来进行左右切割,不像中序数组有明确的切割点,切割点左右分开就可以了。第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)递归函数什么时候需要返回值?
2023-08-13 21:23:18
104
1
原创 代码随想录算法训练营 二叉树part4
题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。判断一个树的左叶子节点之和,那么一定要传入树的根节点,递归函数的返回值为数值之和,所以为int。当遇到左叶子节点的时候,记录数值,然后通过递归求取左子树左叶子之和,和 右子树左叶子之和,相加便是整个树的左叶子之和。如果该节点的左节点不为空,该节点的左节点的左节点为空,该节点的左节点的右节点为空,则找到了一个左叶子。递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0。
2023-08-11 21:29:15
101
1
原创 代码随想录算法训练营 二叉树part3
所以左子树的节点总数我们可以直接得到,是 2^left - 1,加上当前这个 root 节点,则正好是 2^left。返回值: 返回 此树的深度 ,即 max(maxDepth(root.left), maxDepth(root.right)) + 1。计算节点 root 的 左子树的深度 ,即调用 maxDepth(root.left)。显然,此树的深度 等于 左子树的深度 与 右子树的深度中的 最大值 +1。当 root 节点左右孩子有一个为空时,返回不为空的孩子节点的深度。
2023-08-10 19:44:44
119
1
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅