- 博客(30)
- 收藏
- 关注
原创 LeetCode HOT 100:3. 无重复字符的最长字串
3. 以i-1字符为结尾的最长无重复子串的长度tmp与i - left + 1(当前字符与上一个重复字符之间的长度)的关系:如果tmp >= i - left + 1,说明以i-1字符为结尾的无重复最长子串中包含了i字符的重复字符,又因为tmp表示的是i-1无重复,所以tmp可以无痛(隐含的left指针就是i字符前一次出现的地方+1)更新为i - left + 1;4. 根据map中的记录,找到上一个与i字符相同的位置(这里要注意,因为是顺序遍历i,所以在考虑i时,i之前的字符都已经被考虑过了)。
2024-05-25 10:32:35
331
原创 代码训练营Day.48 | 198. 打家劫舍、213. 打家劫舍II、337. 打家劫舍III
因为同时考虑头元素和尾元素的最终答案,在减去头尾中的最小元素后,与nums中去掉头元素而成的基本打家劫舍问题的最终答案,是完完全全的两个概念。即dp[i][j] = dp[i - 1][j] + bdp[i - 2][j - nums[i]] + nums[i](由于不能在相邻房子中偷盗,所以偷盗当前房子时,应该加到上上一行的数上)3. 单层递归逻辑:计算左右孩子的dp数组,根据左右孩子的dp数组,计算当前节点的dp数组。1. dp数组含义:dp[i]表示包括前i个物品的情况下可盗取的最大金额。
2024-01-30 10:18:50
988
原创 代码训练营Day.38 | 509. 斐波那契数列、70. 爬楼梯、746. 使用最小花费爬楼梯
2. 递推公式:bp[i]取决于两种方案,即(爬到i - 1阶所需最小花费)bp[i - 1] + (从i - 1阶走一步)cost[i - 1]和(爬到i - 2阶所需最小花费)bp[i - 2] + (从i - 2阶走两格)cost[i - 2]中最小的那一个。1. bp数组即下标:参照cost数组,bp[i]表示爬到第i阶所需最小花费。3. bp数组初始化:bp[0] = 0、bp[1] = 0;所以,f(n) = f(n - 1) + f(n - 2);4. 遍历顺序:顺序遍历;
2024-01-19 15:40:44
395
原创 代码随想录Day.36 | 435. 无重叠区间、763. 划分字母区间、56. 合并区间
2. 假如存在另一堆区间,这堆区间有着与上一堆区间不同的最小区间,是否可以证明这两堆区间最终要移除至只剩两个区间?假如有两堆各自有最小公共区间的区间,如果只剩余一个区间a,那么这个区间是有可能与两个最小公共区间重叠的,但是,一定可以找到另外两个区间b,c,其中,b,c和a的公共区间不同。然后根据起始坐标从大到小排列,如果起始坐标相等,按照末尾坐标从大到小排列后,遍历区间,依次吞并可以吞并的区间。3. 所以,使得剩余区间互不重叠的最小移除数目,是区间总数 - 最小重叠区间个数。这就跟上一个的复杂解法类似了。
2024-01-17 17:03:05
399
原创 代码训练营Day.35 | 860. 柠檬水找零、406. 根据身高重建队列、452. 用最少数量的箭引爆气球
每次记录一个范围region,表示当前气球之前的公共区间。此时,可以从后往前考虑,一个人后面的身高一定是比他高的。这时只需要根据当前人的h来判断向后要移动多少步,此时,要考虑该元素前面有多少个相同点,以及他本身的h。那么依次遍历,根据当前人的h,作为其下标,插入到对应下标下,因为当前人之前一定是身高大于等于他的人。将气球按照其起点从小到大排列,若相等,按照其大小从小到大排列。需要考虑的情况很少,直接记录三种面值的数量,一一判断即可。那么,从后往前遍历时,h就是当前人要向后移动的步数。
2024-01-16 19:36:53
409
原创 代码训练营Day.34 | 1005. K次取反后最大化的数组和、134. 加油站、135. 分发糖果
然后,将极小值点设为1(颗糖果),从极小值开始,向左爬升到极大值点,糖果数量依次+1,向右爬升到极大值点,糖果数量一次+1。因为如果在计算局部累加和时,count < 0,则说明其对应累加初始坐标一定不是循环起始点,累加和之间的坐标,也不是!因为start到末尾的累加和中,从来没有小于过0,所以,start一定是第一个。还有更简单的是,记录累加和初始坐标start,这样可以在一遍遍历后得到答案,而且只需一次遍历。3. 从右向左遍历,如果当前孩子评分比后一个高,取他原本的数/后一个糖果数+1中最大的。
2024-01-15 16:25:43
1020
原创 代码随想录Day.31 | 455. 分发饼干、376. 摆动序列、53. 最大子序和
有两种情况:1. 该阶段中的负数累加到sum上后的值<0,则刷新累加和sum,和局部累加和sumb。2. 改变连续子区间的起点和终点,在子区间累加和<0后,使子区间累加和=0,相当于重新设置起点;其中,for循环时由饼干的数组控制的,原因在于,如果当前小孩和饼干不匹配的话,即当前饼干不是能满足小孩的最小尺寸饼干,应该用下一块饼干去匹配当前小孩。假如用,下一个小孩去匹配当前饼干,而在当前小孩都不匹配的情况下,下一个胃口更大的小孩更不可能匹配,这就导致在一个for循环下,找不到任何一对匹配的。
2024-01-12 19:59:53
855
原创 代码训练营Day.28 | 93. 复原IP地址、78. 子集、90. 子集II
以下方法直接在s上操作,考虑将三个‘.’插在字符串的哪里。注意第三个点插在最后一位的情况。太简单了,就是组合但是不考虑长度,任何都能压入最终答案中。麻烦的地方在于文本的各种限制条件、剪枝等等。字符串切四刀,最后一刀必须是在末位。有了重复元素,就不得不先排序了。
2024-01-09 19:14:09
405
原创 代码训练营Day.27 | 39. 组合总和、40. 组合总和II、131. 分割回文串
3. 单层递归逻辑。遍历数组,将每一个数当作当前递归层的候选值,然后进一步递归。因为可以重复使用,所以下一层的起点仍然是当前值的位置而不用+1;比较难想到,但仍然是组合问题,组合总和问题是本层递归要选哪个数字为候选值,分割回文串问题是本层递归要分割前几个合为回文串的字符作为候选字符串。与其他组合总和不同的是,数组中含有相同数字,而仍然要求两种答案不可重复。与其他组合总和题目不同的是,这一次数组中的数字可以重复使用。每一层的要割的回文串起点,取决于上一层递归割的终点。注意,在跳过重复数字时,不要额外i++。
2024-01-08 11:35:37
344
原创 代码训练营Day.25 | 216. 组合总和III、17. 电话号码和字母组合
从start到数值9,每个数都充当一遍该层回溯的加项。(按照迭代法来看,就是三指针求和问题,这里有递归来实现);此层递归的加项考虑完后,将其从result中pop出来,完成回溯。如果(k == 0 && n == 0)则将vector<int> result,push到vector<vector<int>> results。参数:([1, 9]中的某个数充当遍历)起点start,还剩几个数k,值还差多少n。普普通通组合问题,用回溯,最麻烦的地方是2~9所对应的字母如何映射,我这里的笨办法是分别考虑。
2024-01-06 10:02:27
380
原创 代码训练营Day.24 | 77. 组合
3. 单层递归逻辑。从start开始到n,每个数都充当一次第一项(具体是第几项,取决于递归深度),push到result中。然后在,后序区间找k - 1个数。最后,将result中最后一个数pop出来,完成回溯。vector<int> result 中的数恰好够最初的k个,或者当前递归要找0个数。在[1, n]中找k个数的组合,一般的想法就是取区间首项作为第一个数,然后在剩余数中找(k - 1)个数。然后取区间第二项作第一个数。1. 返回值和参数。参数:区间起始值start,区间结束值n,要找k个数。
2024-01-05 15:48:46
365
1
原创 代码随想录Day.23 | 669. 修剪二叉搜索树、108. 将有序数组转换为二叉搜索树、538. 把二叉搜索树转换为累加树
找恰好大于low的节点时,即用正常左中右的中序遍历,pre指针指向前一个,判断root是要找的节点条件是(pre!= NULL && pre->val < low && root->val >= low)此时,root根本不是要删除的,而删除pre比较困难,而且可能存在root是pre右子树中的值这种情况,处理起来更麻烦。针对情况3,说明要保留的部分树的根节点,就是rootbenshen。针对情况3,要在root树中,假如某个节点恰好小于low,则要删除该节点及其左子树,即令其右子树替代他;
2024-01-04 12:32:12
867
1
原创 代码训练营Day.22 | 235. 二叉搜索树的最近公共祖先、701. 二叉搜索树中的插入操作、450. 删除二叉搜索树中的节点
更巧妙的是,删除当前节点后,最合适的候选节点是左子树最右节点或者右子树最左节点。细想的话,其实如果将左子树上位,右子树一定被插在左子树中的最右节点的右节点上;如果让右子树上位,左子树一定被插在最左节点的左节点上。其实这道题目可以看成左子树上位后,将右子树插入左子树,因为两边都是二叉搜索树,且左子树必小于右子树,所以只要简单插入即可。即,root的val在p、q的val所构成的闭区间内。相当于将在遍历至NULL的路上,重新连接原本的树,直到遇到NULL,然后把NULL换成val。参数:当前节点和val。
2024-01-03 11:34:14
363
1
原创 代码训练营Day.21 | 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先
就是找到p、q节点。如果,恰好左右节点分别在某节点的左右子树上,直接返回这个节点,即为公共节点。中序遍历,记录前一个指针,并记录前一个指针和当前指针的绝对差值。自己想到的笨办法,自顶向下找,每次都要遍历一遍当前节点之下的节点。从下往上遍历,就用后序遍历,先判断完左右子树,然后根据结果判断当前节点。中序遍历,记录指针、最大出现频率、当前数字累计个数、最终result。
2024-01-02 16:19:29
321
原创 代码训练营Day.20 | 654. 最大二叉树, 617. 合并二叉树,700. 二叉搜索树中的搜索,98. 验证二叉搜索树
记录最大值或者上一个结点,及在中序递归时,如果是二叉搜索树,下一个要遍历的结点值必大于当前值。迭代法,与判断二叉树是否对称相似,两个两个走,注意,先判断是否两个都不是NULL而可以push,再考虑root1子树是NULL的情况而替换成root2子树。1)递归,一棵树是否为有效二叉搜索树,取决于两个条件:1. 两棵子树都是二叉搜索树;2. 左子树的最大值小于root->val,右子树的最小值大于root->val。与知道中序和后序数列的情况下啊构造树一样,将左右区间标出更省空间。迭代,普普通通遍历。
2024-01-01 17:56:22
378
原创 代码训练营Day.18 | 513. 找树左下角的值、112. 路径总和、106. 从中序与后序遍历序列构造二叉树
简简单单递归,每次只找当前根节点,而根节点总是在postorder最后一个。与之前的路径字符串输出一样,也与上题一样,深度递归,新添一个一维vector<int>,如果符合题目,则加入到二维vector<vector<int>>。用深度递归,当判断为叶子节点,且路径总和等于target时,返回真实路径总和,否则返回不可能是target的值。因此,1. 从右向左递归,最终值一定被最左叶子节点覆盖;用深度递归,参数量更少的办法是,target可以自顶向下减,而且返回值可以直接是bool型。
2023-12-30 17:29:14
421
1
原创 代码训练营Day.17 | 110. 平衡二叉树
关于自下而上求高度的递归,还有比较巧妙的解法,即该节点是平衡二叉树,则返回正确高度,如果不是,则返回-1。因为一棵树的高度不可能是-1,所以可以这么搞。否则,只递归入非NULL子节点,且在进入前path加入“->”。代码随想录中,提到回溯,其实就是进入left后再进right时,path值并没有被left影响。其次明确,二叉树的高度和深度表示什么。只有当判断出当前节点的左子节点是叶子节点时,才加到result中。参数:树节点和路径。普通递归遍历,只加左叶子节点,其他都当0加。由下往上的递归方法,是首选。
2023-12-29 20:14:04
848
1
原创 代码训练营Day.16 | 104. 二叉树的最大深度、111. 二叉树的最小深度、222. 完全二叉树的节点个数
简单来说,层序遍历中,遇到空节点时,更新依次最小深度记录。(这就要求最小深度初值一定比树大,根据题中树节点数的范围最大100000,则depth初值可以设置为100000).有两种算最大深度的方法,一种是自顶向下depth叠加,另一种是自底向上depth叠加。但核心逻辑就是,当前树的最大深度,是其左右子树中深度最大的一个depth + 1。当前树节点不是NULL,说明在更下一层深度,depth++。需要过滤,只有一个NULL节点的,就不考虑其NULL分支了。参数:树节点和当前节点所在层的深度depth。
2023-12-28 18:11:51
935
1
原创 代码训练营Day.15 | 102. 二叉树的层序遍历、226. 翻转二叉树、101. 对称二叉树
前序遍历的递归方法你应该很熟了,基本上递归的逻辑顺序就是前序遍历的顺序。做完之前的层序遍历,乍一看可以在上一层节点下,倒叙连接下一层的节点。我们只要知道上一层有多少节点就行,因为,在将某一节点的左右孩子压入队列时,如果有孩子,那这一层的节点数目就++,这样层层往下。而第一层的数目永远知道。这里有一个偷懒小技巧:因为其实每层的节点数目,就等于队列长度,(上一层的已经全pop掉了,下一层的还没开始),所以不用额外空间记录每层长度。这个不是普通的利用队列实现的层序遍历,难点在于同一层的节点数值要包在一起。
2023-12-28 10:33:05
1058
1
原创 代码随想录Day.14 | 递归遍历、迭代遍历、统一遍历
循环中,每个栈顶的非空节点,都进行访问,然后根据前中后,考虑把该节点再以什么顺序压入。中,先右,再自己,后左;后,先自己,再右,后左。但是,比较巧妙的是,后序遍历反过来就是“父右左”的顺序,这和前序遍历“父左右”,事实上异曲同工。进一步说明,在前序和后续遍历中,从栈中pop出来的节点,既要继续访问其子节点(push),又要记录其本身数值(result.push_back)。代码逻辑是,先持续访问left到底(push到栈中),当NULL了,从栈中pop一个,记录,然后访问right(push到栈中)。
2023-12-27 16:57:56
1269
1
原创 代码训练营Day.13 |
这话说的,啥叫潜在最大值。事实上,就是,每进来一个新的元素,都把他前面比他小的“拉”出去,(需要用到可以从队尾pop的队列)。每次都去遍历一次队列,找最大值,当然是最直接最暴力的解法。时间复杂度的解释,虽然每个元素会有pop_back操作和push_back操作,但是,在pop_back前,还有比较操作呀?但是,细想,比较操作好像,并比不了几次。注意,sort在类成员函数中,定义的排序函数也在类成员中,注意static。但是不可能又元素一直成为最后一个,如果他老是最后一个,那其他人就不会是最后一个了。
2023-12-26 19:05:34
989
1
原创 代码随想录Day.11 | 20. 有效的括号、1047. 删除字符串中的所有相邻重复项、150. 逆波兰表达式求值
因为是连连看的特性,能少些两行代码。因为在碰到左括号是,压入栈的是其对应的右括号,则在连连看时,仅需判断栈顶字符是否和当前s[i]相等即可。s[i] == ')' && r.top() == '('就不用写这么多字了。需要注意的是,执行减法、除法是,区分哪个数字是被减/除数,哪个是减除数。而如果是true的,一定会抵消完所有括号,因为总有相邻同类型的左右括号。做过括号连连看、删除相邻相同字符连连看,就知道,他俩这种。而,这道逆波兰表达式求值,其实仍然是连连看进阶版。栈,非常适合用来做这种连连看。
2023-12-23 10:37:16
349
1
原创 代码训练营Day.10 | 232. 用栈实现队列、225. 用队列实现栈
两个栈,简单一点就是一个s1用来push,一个s2用来pop。pop或者peek的时候,s2如果没东西,就先从s1拿点过来。细想一下,一个队列就够了。
2023-12-22 20:29:46
374
原创 代码训练营Day.7 | 454. 四数相加II、383. 赎金信、15. 三数之和、18. 四数之和
1. 第一个数——i,因为排序之后总会有重复数字相连,而每种数字的第一个在充当i时就会把所有跟nums[i]相关的都考虑完全。所以,每种数字的第一个充当完i后,i应该忽略考虑后续重复数字。2. 第二、三个数字同样遵循这个道理,每种数字的第一个充当完left、right后,应该忽略考虑后续重复数字。根据nums[i] + nums[left] + nums[right]的大小,移动left和right。两重循环,第一重是三数中的第一个数,从最小数开始,一次右移充当这三个数中的第一个数。
2023-12-22 19:35:34
351
原创 代码训练营Day.8 | 344. 反转字符串、541. 反转字符串II、54. 替换数字、151. 翻转字符串里的单词、55. 右旋字符串
相比创建一个新的字符串来存放替换好的字符串,更节省的方法是resize原先字符串的长度,此时要注意,原本就有一个count的长度(即原本为数字的地方),此时只要再拉长(count * 5)即可,即最终长度为(s.size() + count * 5)首部多余空格,j == 0 && s[i] == ' '时,continue;单词之间多余空格,s[i] == ' ' && s[i - 1] == ' '时,continue;卡码网的代码,首先要注意我们要另外写输入输出逻辑,而且是循环的那种。
2023-12-21 20:04:48
842
原创 代码训练营Day.6 | 242. 有效的字母异位词、349. 两个数组的交集
0”,这样在n=1时,即最高位数字为1时,1 / 10 == 0。注意map.insert的用法,需要“map.insert(pair<int, int>(key, value))”所以,记录每次的快乐化结果,如果有跟之前重复的,就是false。如何创建,如何查找,如何删除。
2023-12-18 18:14:23
345
1
原创 代码训练营Day.4 | 24. 两两交换链表中的节点、19. 删除链表的倒数第N个节点、面试题 02.07. 链表相交、142. 环形链表II
1. 先考虑是否需要dummyHead,比如head会变的情况下,需要。2. 注意避免NULL->next。
2023-12-16 11:17:50
736
1
原创 算法训练Day.2 | 977. 有序数组的平方、209. 长度最小的子数组、59. 螺旋数组II
如果右闭(sum += nums[++right]),则吃到最后一个时还能继续拉。此时还想继续吃时(此时已经没有能吃的了),要注意出界。所以要先判断出界再吃。区间范围就需要考虑特殊情况了,此题仅一种,即右指针已经到最后,但最小长度还可以缩短。窗口内数字如果够了,就拉出一个,试着让长度最小。很明显,如果留一步,即起点从原点开始,终点到末尾前一格,一圈所走的四个方向写法很规律。如果走到头,一圈的四个方向都各不相同,需要分别考虑。找规律,一圈一圈写,此时需要考虑每次走某行或者某列时,是顶到头还是留一步。
2023-12-14 20:23:37
359
原创 算法训练Day.1 | 704. 二分查找、27.移除元素
如果情况为3,此时需要分情况讨论,如果右开,即right移动到了mid上,而mid(==left==right)刚被判断为!如果右闭,left == right时,right下的总是没被考虑过的数据,因此还不能跳出去,此时,1. while(left<=right);在相邻情况下,由于mid = (left + right) / 2,小数部分会被舍去,无形之中,mid更容易偏向于left。而事实上经过多轮循环,极限情况5会转化为极限情况1或2,所以一共有四种极限情况1,2,3,4。
2023-12-13 21:38:08
832
1
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人