代码随想录一刷总结

目录

数组

题35 搜索插入位置

有序数组就想到二分查找,没有重复元素,查找下标唯一。写的时候要注意边界的判断。

题27 移除元素

在数组和链表的操作中,快慢指针非常常见。本题中,只有当快指针的值不是要删除的元素,才会赋值给慢指针。

题977 有序数组的平方

平方的最大值一定在数组的两端出现,双指针可以巧妙解决。

题209 长度最小的子数组

用双指针,也可以叫滑动窗口。当子数组不够大时,移动快指针来放大窗口;当子数组大于target时,移动慢指针来缩小窗口。

题59 螺旋矩阵2

按上右下左进行循环,每循环一次,对应行(或列)要向内缩小一次。

链表

题203 移除链表元素

设置一个首结点,让所有位置的操作统一。熟悉链表的写法。

题707 设计链表

链表的基本操作。

题206 反转链表

迭代:双指针法,保存当前结点的下一结点,然后将当前指针指向前一结点。
递归:本质还是双指针。

题24 两两交换链表中的结点

手动模拟过程,需要提前记录后面的结点。

题19 删除链表的倒数第n个结点

快慢指针,快指针先走n步。

面试题0207 链表相交

双指针法很巧妙,两指针各自遍历完两条链,若相交,则一定同时到达交点。

题142 环形链表2

快慢指针,快指针每次走两步,慢指针走一步,如果有环,两者一定在环内相遇。
此时一指针从头结点出发,一指针从相遇结点出发,两者相遇处即为环入口。

哈希表

要快速判断一个元素是否出现在集合里,或者是否出现过,就要考虑哈希法。题目如果未限制数值大小,就不适合用数组。如果是返回索引下标不能用双指针法,返回数值则可以。

题242 有效的字母异位词

建立一个s中字符出现次数的哈希表(数组),再减去t中字符出现次数,最后观察哈希表中次数是否都是0。

题349 两个数组的交集

建立数组1的哈希表(字典),再遍历数组2,如果出现在哈希表中,则添加到结果中。
本题直接用集合来做也可以。

题202 快乐数

建立哈希表,一个数如果出现过,则返回false。

题1 两数之和

遍历数组,如果一个数它的对应数不在哈希表中,则将这个数插入哈希表中,否则输出下标。

题454 四数相加2

对a b数组的和建立哈希表,对c d数组的和进行遍历,将和为0的值记入结果。

题383 赎金信

用数组作为哈希表。

题15 三数之和

先排序数组,通过i,i+1,right三个指针来搜索满足条件,注意要跳过重复解。

题18 四数之和

与上题方法类似,只不过多加一层循环。注意target是不固定的,所以不能和上题一样去重。

字符串

题344 反转字符串

交换法,双指针法均可。

题541 反转字符串2

间隔2k来处理。Python中字符串不可变,要将字符串转列表处理。

剑指offer 05 替换空格

双指针法。先给数组扩容到填充后大小,再从后向前操作。

题151 翻转字符串中的单词

先去掉多余空格,然后翻转整个列表,最后对每个单词进行翻转。

剑指offer 58 左旋转字符串

利用取模操作。

题28 实现strStr()

本题关键在于理解KMP算法。
当原串和模式串不匹配时,会跳到模式串的最长相同前缀后的位置进行匹配,如果仍不匹配,则才从模式串开头重新匹配。
需要事先构建出模式串的next数组。即最大相同前后缀的长度。

题459 重复的子字符串

需要用到KMP算法。先构造next数组,j从0,i从2开始取。len(s)如果是len(s)-next[-1]的整数倍,则是重复子串。(当然next[-1]需要大于0)

栈和队列

题232 用栈实现队列

需要一个输入栈和输出栈。输入栈用于append元素。当需要输出队列头时,若输出栈不空,则只要弹出输出栈顶元素;若输出栈为空,则需要将输入栈所有元素添加到输出栈,再弹出栈顶元素。
如果只需要返回队列头的值,则可以先pop操作,再将这个元素压入输出栈。
判空时,需要两个栈均为空,队列才为空。

题225 用队列实现栈

只需要一个队列即可(用双向队列但只使用popleft和append操作)。弹出栈顶元素时,需要将队列元素先依次弹出,并添加至队列尾,最后弹出原队尾元素。返回栈顶元素是利用索引。

题20 有效的括号

栈很适合用来做对称匹配。
当字符为左括号时,将对应右括号入栈,当字符为右括号且与栈顶不同或者栈为空时,匹配失败,否则匹配成功,弹出栈顶元素。最后栈为空则全部匹配成功。

题1047 删除字符串中的所有相邻重复项

利用栈,相同就弹出栈顶,不同就入栈。

题150 逆波兰表达式求值

后缀表达式,遇到数字就入栈,遇到操作符则从栈顶弹出两个元素并计算结果,将结果入栈。
注意写法eval(f’{num1} {i} {num2}')可以直接运算求值。

题239 滑动窗口最大值

使用单调队列(递减)来模拟。队列头存储最大元素的下标。队列满就将队头移出。如果队尾元素比当前元素小,则依次移出队尾元素,最后将当前元素入队。

二叉树

•递归的三要素:确定递归函数的参数和返回值,确定终止条件,确定单层递归的逻辑。
•求高度用后序遍历,求深度用前序遍历。
• 如果需要遍历整棵树,且不用处理递归返回值,递归函数就不能有返回值。如果需要遍历某一固定路线,就一定要有返回值。
• 用迭代方式记录所有路径比较麻烦。
• 构造树一般采用前序遍历。
• 如果让空节点进入递归,就不加if。如果不让,就加if限制一下,两种终止条件不同。

题144 前序遍历迭代法

利用栈,根节点先入栈,然后出栈访问结点值,接着非空的右结点,左结点相继入栈。

题94 中序遍历迭代法

根节点先不入栈,先迭代访问最底层的左子树结点,然后访问其父节点,然后取右结点。

题145 后序遍历迭代法

调整前序遍历的代码顺序,最后反转result数组。

空指针标记法(统一写法)

访问但还没处理的结点,添加空节点作为标记。只有遇到空节点,才将下一个结点放进结果集。

题102 二叉树层序遍历

利用队列,队列头出队时,访问该结点,并将左右非空结点入队。
注:stack = [root]和que=deque([root])的差异。

题107 二叉树的层次遍历2

最后反转结果即可。

题199 二叉树的右视图

只输出每层的最后一个结点。

题637 二叉树的层平均值

每层求和并求均值。

题429 N叉树的层次遍历

extend(cur.children)。

题515 在每个树行中找最大值

max操作。

题116 填充每个结点的下一个右侧结点指针

每个结点的next指向队列头。注意跳出循环。

题117 填充每个结点的下一个右侧结点指针2

同上。

题104 二叉树的最大深度

遍历完一层,深度+1。

题111 二叉树的最小深度

当某结点左右子树都空,就跳出循环。

题226 翻转二叉树

递归写法,层次遍历迭代法。

题101 对称二叉树

两棵树对称,不是两个结点对称。left.left 和 right.right,left.right 和 right.left.
迭代法要借助队列。

题222 完全二叉树的节点个数

当成普通二叉树用层次遍历很简单。
完全二叉树可以用递归来做,当左右子树为满二叉树时,可以用公式来计算结点数。

题110 平衡二叉树

递归写法很简单,只要不满足平衡性,就返回-1,否则就计算该子树的高度。

题257 二叉树的所有路径

递归+回溯,当非叶子结点时,继续递归,注意回溯要紧接着递归,当为叶子结点时,将路径添加到结果集合。

题404 左叶子之和

递归法,左叶子结点值,加上左子树左叶子和,加上右子树左叶子和。
迭代法,借助栈。

题513 找树左下角的值

层次遍历。

题112 路径总和

递归法,只查找某条路径所以一定要有返回值,使用target值减去结点值,如果到达叶子结点时,值为0则说明找到。如果叶子结点返回true,则函数立即返回true。
迭代法,需要两个栈,第二个栈用来存放target值。

题113 路径总和2

递归+回溯,注意res.add(new ArrayList<>(paths))。

题106 从中序与后序遍历序列构造二叉树

使用递归,根据后序最后一个元素先找分割点,然后再分割中序、后序,接着递归遍历。

题105 从中序与前序遍历序列构造二叉树

与上题类似。

题654 最大二叉树

递归的分割数组,还是只对索引进行操作,可以节省时空开销。

题617 合并二叉树

当一棵树为空了,直接返回另一棵树。

题700 二叉搜索树中的搜索

利用二叉搜索树的特性,写递归和迭代都很方便。

题98 验证二叉搜索树

简单做法是中序遍历得到数组,然后验证数组是否从小到大。
使用递归效率更高,设置一个前结点,遇到不符合的就返回false。

题530 二叉搜索树的最小绝对差

要会在递归中记录前一个结点的指针。TreeNode pre。

题501 二叉搜索树中的众数

使用了maxCount,结合集合的clear操作从而只需要遍历一次。

题236 二叉树的最近公共祖先

使用后序遍历的递归,要知道怎么一层层把值给返回的。

题235 二叉搜索树的最近公共祖先

最近公共祖先一定是值在两个结点之间的值。

题701 二叉搜索树的插入操作

递归法要用root.left等接住返回值。迭代法比较简单。

题450 删除二叉搜索树的节点

删除要考虑删除结点的几种情况。

题669 修剪二叉搜索树

先返回符合条件的结点,然后上面再接收结点

题108 将有序数组转为二叉搜索树

选取数组中间的元素作为分割点。

题538 把二叉搜索树转换为累加树

按右中左的顺序遍历。

回溯

• 组合问题需要index,不同集合取元素是index+1,同一集合取元素是i+1.
• 排列问题不需要index,因为每次都从头(从下标0)开始选元素。排列问题需要对树枝去重,也就是路径上去重,已经选过的元素不再选,需要用到pathUsed数组来标记,元素不重复时也可以直接用path.contains()来判断。
• 对同一层进行去重操作,也可以叫剪枝,通常需要的是一个映射,相同的值不会被重复取,可以用数组来做映射。可以排序的情况下,可以用i和i-1的元素是否相同来做。
• 子集问题不需要return,因为要返回所有的结点。

题77 组合

回溯算法,记模板。可以剪枝。

题216 组合总和3

和上题类似。

题17 电话号码的字母组合

学会做映射,以及字符串的加减。

题39 组合总和

关键在于,数组要排序,for循环要记得及时跳出循环。

题40 组合总和2

要对同层元素进行去重,candidate[i]==candidate[i-1],就跳出本次循环。

题131 分割回文串

回文串用双指针判断。学会字符串的切片操作。

题93 复原IP地址

需要判断区间内字符串是否合法,对原串进行操作,有些细节需要注意。

题78 子集

子集问题是收集树的所有节点的结果。

题90 子集2

需要排序去重。

题491 递增子序列

不能排序,需要用数组记录同一层已使用元素。

题46 全排列

每次要从0开始遍历,需要对路径上元素去重。

题47 全排列2

排列问题需要路径去重,有重复元素需要在同一层去重。

题51 n皇后

在二维数组进行操作,最后将二维数组转为列表添加到结果中。本题关键在于如何将二维数组转为列表,以及怎么判断插入位置合法。本题还需要再做一遍。

题37 解数独

需要用到二维递归。回溯方法需要有boolean的返回值,可以防止棋盘永远填不满。要会判断当前位置插入数字是否合法。

贪心

题455 分发饼干

直接循环就行,可以先满足小胃口的,也可以先满足大胃口的。

题376 摆动序列

其实就是找非单调序列的长度,用一个flag来标记增减。

题53 最大子数组和

当总和为负时,需要重新从0开始计算。

题122 买卖股票的最佳时机2

每个增区间都进行买卖。

题55 跳跃游戏

模拟流程,走完一步每次用(剩余步数,该位置能跳步数)的较大者更新剩余步数

题45 跳跃游戏2

模拟流程会超时。需要记录当前最远覆盖,和下一步最远覆盖。索引只要走到倒数第二位就算成功。最远覆盖随着索引更新。当索引来到当前最远覆盖时,就需要再走一步了,同时更新当前最远覆盖。

题1005 k次取反后最大化的数组和

首先对数组按绝对值从大到小排序,然后从头开始遍历,遇到负数就取反,若最后还剩奇数次k,则对最小的数取反。

题134 加油站

先计算每个站的剩余,如果总剩余为负,则一定不行。如果累积到当前位置的剩余为负,说明至少要从下一个位置开始。

题135 分发糖果

需要先考虑右边评分大于左边,从左向右遍历。然后再考虑左边评分大于右边评分,从右往左遍历。

题860 柠檬水找零

模拟找零过程就行。

题406 根据身高重建队列

本题主要在于排序。首先按身高从大到小排,相同身高的k小的在前。然后再从左到右依次插入即可。

题452 用最少数量的箭引爆气球

两个气球不重叠,就需要多射一支箭。当重叠时,每次都要更新重叠气球的最小右边界。

题435 无重叠区间

和上题类似。当两个区间重叠时,就需要移出一个区间,并更新重叠区间最小右边界。

题763 划分字母区间

首先利用数组做哈希,得到每个字母出现的最远下标。然后遍历字符串,每次更新最远边界,当索引和最远边界相同时,就可以进行分割。

题56 合并区间

两个区间有重叠就更新最大重叠右边界,当不重叠时,把左右边界添加到结果中。因为最后一个区间后面没有不重叠区间,所以需要单独在最后加上。

题738 单调递增的数字

这道题要转为字符数组来操作。从后往前遍历,每当左边大于右边时,就需要将左边的数减一,最后统一将右边的数置为9。

题714 买卖股票的最佳时机含手续费

当卖出股票可以盈利时,就开始累加收益,但此时未必是真正卖出。minPrice = price[i] - fee 这里很巧妙,当后面一天的price高于这里的minPrice,就说明此时并未真正卖出。

题968 监控二叉树

叶子结点不放摄像头,在叶子的父节点放摄像头,然后隔两个结点放摄像头。这道题巧妙的地方在于,设置了三种状态:0表示结点无覆盖,1表示结点有摄像头,2表示结点有覆盖。对于空节点,分析得知应该是有覆盖。最后如果根节点无覆盖,那么需要再放一个摄像头。

动态规划

动态规划五部曲:1.确定dp数组以及下标的含义,2.确定递推公式,3.dp数组的初始化,4.确定遍历顺序,5.举例推导dp数组。

题509 斐波那契数

只用两个位置来存储即可。

题70 爬楼梯

本质还是斐波那契数列。

题746 使用最小花费爬楼梯

dp[i]是从位置i到终点的最小花费,其等于cost[i]+min(dp[i+1],dp[i+2])。

题62 不同路径

初始化很重要,本题要将左和上边界都初始化为1。

题53 不同路径2

和上题类似。初始化时,遇到有障碍物,则应保持dp数组中初始值为0。遍历时遇到障碍物应该跳过此次循环。

题343 整数拆分

这题用贪心效率更高。用dp,就是将dp[i],分成j*(i-j)和j*dp[i-j],取两者中大的。

题96 不同的二叉搜索树

dp[i] += dp[以j为头节点左子树结点数量] * dp[以j为头节点右子树结点数量]。

二维数组01背包

dp[i][j]表示,在背包容量j内,前i个物品可以得到的最大价值。对于物品i,有放和不放两种选择,对应的dp[i][j] = max(dp[i-1][j],dp[i-1][j-wi] + vi)。初始化,i为0时,当背包容量够放物品0时,就应该初始化为物品0对应价值。遍历顺序,先遍历物品,与先遍历背包重量都可以,因为需要的数据都在左上方。

一维数组01背包

本质是在一维数组上反复遍历更新。dp[j]表示容量j内,可以得到的最大价值。对于物品i还是两种选择,dp[j]= max(dp[j],dp[j-wi] + vi)。初始化,都初始化为0即可。遍历顺序,只能先遍历物品,再遍历背包容量,且容量j要从后往前遍历。如果j从前往后遍历,那么物品0可能被重复放多次,而从后往前不会有状态重合。如果先遍历背包容量,再遍历物品,那么每个dp[j]就只会放一个物品。

题416 分割等和子集

可以转为背包问题。背包容量为sum/2,物品的重量和价值都是元素的值。dp[j]表示的是容量j内可以得到的最大价值。当容量为目标值时,如果最大价值等于目标值,则说明可以找到这样的集合。

题1049 最后一块石头的重量2

和上题类似。本题要尽可能让石头分成重量相同的两堆。背包容量为sum/2。最终用两堆石头重量作差得到结果。

题494 目标和

首先要注意如果target+sum为奇数,是无解的。dp[j]表示装满容量为j的背包的方法数。初始化,dp[0]必须初始化为1。dp[j] += dp[j - nums[i]]。

题474 一和零

这题背包容量有两个维度,分别是0和1的容量。

题518 零钱兑换2

完全背包问题,背包容量也正序遍历。对于组合问题,先物品,后容量,因为这样物品的顺序是固定的。对于排列问题,先容量,后物品。

题377 组合总和4

完全背包排列问题。需要先遍历容量。

题70 爬楼梯

可以转化为完全背包排列问题。相当于只用1和2组成n有多少种排列。

题322 零钱兑换

完全背包。求最少,初始化要为最大值。当dp[j-coins[i]]仍为初始值时,就不用更新dp[j]。

题279 完全平方数

和上题类似。容量就是n,数的平方就相当于物品的重量。

题139 单词拆分

单词就是物品,字符串就是背包,就是求物品能不能把背包塞满。dp[j]表示长度为j的字符串可以被拆分。如果dp[i]为true,且[i,j]区间内的字符串出现在字典里,那么dp[j]为true。对于本题,先遍历容量(对字符串遍历),后遍历物品更方便。

题198 打家劫舍

第i家如果偷,就只考虑前i-2家,不偷就考虑前i-1家。

题213 打家劫舍2

要么在[0,n-2]中偷,要么在[1,n-1]中偷。

题337 打家劫舍3

树型dp。因为需要通过递归函数的返回值做下一步计算,所以一定用后序遍历。定义一个长度为2的数组[不偷,偷],用来保存不偷该节点,和偷该节点所能得到的最大价值。

题121 买卖股票的最佳时机

dp[i][0]表示第i天持有股票的最大利润,dp[i][1]表示第i天不持有股票的最大利润。最终结果是dp[length-1][1],因为本题不持有股票一定比持有股票利润高。本题用贪心做更方便。

题122 买卖股票最佳时机2

和上题区别在于可以多次买卖。使用贪心更方便。

题123 买卖股票最佳时机3

股票有5个状态:无操作,第一次买入/卖出,第二次买入/卖出。然后根据当天有无买卖写出递推关系。

题188 买卖股票最佳时机4

上一题的升级版。k次交易就最多有2k个买卖状态。第二层循环要对状态进行遍历,奇数对应买入,偶数对应卖出。

题309 买卖股票最佳时机含冷冻期

dp[i][0],dp[i][1]分别表示持有股票,不持有股票的最大利润。需要初始化两天,dp[1][1]一定是非负的,需要在p(1-0)和0中取最大值。持有股票状态在更新时,如果当天买入,应该要从i-2天卖出状态计算,因为有一天的冷静期。

题714 买卖股票最佳时机含手续费

当天卖出时要减去手续费。

子序列问题

• 只有一个序列/数组时,定义dp[i]是到下标i即可。当有两个序列时,定义dp[i][j]是到下标i-1,j-1更有利于初始化方便,因为只需要置0就好。
• 要求是连续子序列时,只需要看前一个下标的状态即可。可以是不连续时,就需要考虑更多下标。

题300 最长递增子序列

dp[i]表示以nums[i]结尾的最长递增子序列。dp数组需要初始化为1。dp[i]等于i之前的dp[j]的最大值+1。

题674 最长连续递增序列

和上题类似,但只需要前一个的状态。

题718 最长重复子数组

子数组就是连续子序列。定义dp[i][j]是以下标i-1,j-1结尾的两个数组的最长重复子数组,这样定义方便初始化,只需要都初始化为0即可。

题1143 最长公共子序列

dp[i][j]表示长度为i-1,j-1的字符串的最长公共子序列。注意本题和上题递推公式的差异。

题1035 不相交的线

和上题一模一样。

题53 最大子数组和

dp[i]表示以i结尾的最大子数组和。dp[i]的来源有两个。

题392 判断子序列

这道题可以完全当做求最长公共子序列来做。也可以在i-1和j-1不同时,只对j作减法。本题也可以用双指针做。

题115 不同的子序列

dp[i][j]表示,到i-1的s的子序列中,出现到j-1的t的个数。初始化,dp[i][0]要初始化为1。当最后一个元素相等时,可以选择用s[i-1]来匹配,也可以选择不用。

题583 两个字符串的删除操作

dp[i][j]表示到i-1,j-1的字符串要删除的最少次数。初始化很重要。本题也可以求出最大公共子序列,然后用长度作减法。

题72 编辑距离

当最后一个字符不同时,有三种操作:删除一个w1的字符,删除一个w2的字符,和把一个w1的字符替换掉。

题647 回文子串

dp[i][j]表示下标[i,j]之间的字符串是不是回文串。当字符i和j相等时,如果j-i的值小于等于2,那么一定是回文串,否则就需要去看dp[i+1][j-1]的值。遍历顺序必须从下往上,从前往后。
也可以用双指针,要么是单个元素i,要么是相邻元素i和i+1,同时向往扩。

题516 最长回文子序列

dp[i][j]表示i和j之间的最长回文子序列长度。初始化,需要将对角线上元素置为1。最后输出为0到n-1的的结果。

单调栈

通常是一维数组,要找一个元素右边或者左边第一个比它大或者小的元素的位置,想到用单调栈。单调栈里存放的是下标。顺序是指从栈顶到栈底的顺序。

题739 每日温度

要找下一个比它大的,那么顺序是递增的。对于小于等于栈顶元素的直接入栈,而大于栈顶的则需要先计算距离并出栈,然后再入栈。

题496 下一个更大元素1

首先结果数组都初始化为-1,然后要建立一个哈希表,key是数组1的值,value是对应的下标。对于小于等于栈顶元素的直接入栈,而大于栈顶的,需要看栈顶元素是否在数组1中出现,没有出现就直接弹出,如果出现了就需要先取得栈顶元素在数组1中的下标,然后对结果数组进行赋值,注意,当前元素就是第一个更大元素。最后将当前元素入栈。

题503 下一个更大元素2

走两遍nums数组。

题42 接雨水

单调栈做法,遇到比栈顶小的就入栈,相等的先出栈,再入栈。当比栈顶大时,先出栈栈顶作为底边,此时若栈不空则栈顶作为左边界,然后计算体积。直到while循环结束,再把当前元素入栈。
本题也可以用双指针和动态规划来做,主要都是找到每个位置的左边和右边的最高柱子,然后利用短的来计算体积。

题84 柱状图中最大的矩形

单调栈需要在最前面和最后面分别插入一个0。此时是要求单调栈递减的。即找到当前柱子的左右第一个小于它的柱子来计算面积。此题需要再做一遍。

  • 12
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值