![](https://img-blog.csdnimg.cn/20201014180756780.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
LeetCode刷题笔记
文章平均质量分 60
刷LeetCode中写的笔记
wasamtc
这个作者很懒,什么都没留下…
展开
-
数组前缀和
在这一道题目里面,中间某一段连续子数组和为k,意思即为sum[i] - sum[j - 1] = k,也即sum[i] - k = sum[j - 1],所以当我们求每一个的前缀和的时候,只需要统计对应的sum[j - 1]个数即可。前缀和就是指前缀的和,例如在数组中,从开始到 i 就是到 i 的前缀和。前缀和一般用来求中间连续某一段的和,例如sum[i] - sum[j - 1]就可以求出j 到 i 这一段的和。当然还有二维数组的前缀和,不过大致原理就是这样,通过两个前缀和求中间的和。原创 2023-07-18 16:37:55 · 421 阅读 · 0 评论 -
372.超级次方
LeetCode超级次方:你的任务是计算 ab 对 1337 取模,a 是一个正整数,b 是一个非常大的正整数且会以数组形式给出。原创 2023-01-28 15:27:25 · 105 阅读 · 1 评论 -
leetcode:4.寻找两个正序数组的中位数
给定两个大小分别为 m 和 n 的正序(从小到大)数组nums1 和nums2。请你找出并返回这两个正序数组的 中位数 。算法的时间复杂度应该为 O(log (m+n)) 。原创 2022-11-01 09:40:04 · 107 阅读 · 0 评论 -
并查集总结
并查集是一种树型的数据结构,用于处理(不相交)集合的合并、查询及由此衍生的问题。其功能主要有三个:1. 寻找根节点,函数:find(int u),也就是判断这个节点的祖先节点是哪个2. 将两个节点接入到同一个集合,函数:join(int u, int v),将两个节点连在同一个根节点上3. 判断两个节点是否在同一个集合,函数:same(int u, int v),就是判断两个节点是不是同一个根节点原创 2022-10-15 10:04:14 · 92 阅读 · 0 评论 -
单调栈总结
所谓单调栈,指的是栈中的值保持着单调,当有一个新值到来时,需要比较新值与栈顶元素,如果不符合单调性,需要一直弹出栈顶元素直至符合单调性或者栈空才能压入新值。单调栈一般用来求左边或右边第一个比自己大或小的元素的位置。使用单调栈主要要想清楚三种情况,当新值大于等于小于栈顶元素的情况。原创 2022-10-14 16:16:17 · 385 阅读 · 0 评论 -
动态规划总结
动态规划区别于贪心的是它的当前每一个状态(除了初始化)一定是由上一个状态推导出来的。 动规解题步骤:1:确定dp数组及其下标的含义;2:确定递推公式;3:dp数组如何初始化;4:确定遍历顺序;5:举例推导dp数组。原创 2022-10-11 11:03:50 · 140 阅读 · 0 评论 -
贪心算法总结
贪心的本质是选择每一阶段的局部最优,从而达到全局最优。难点在于如何通过局部最优推出全局最优。如果感觉能用贪心的话可以手动模拟试一试。我觉得贪心的套路就是当前要保持一个什么最值,然后遇到什么情况要更新这个最值。原创 2022-09-25 11:18:44 · 380 阅读 · 0 评论 -
回溯算法总结
回溯算法本质是一种穷举算法(可以通过剪枝来提高效率),回溯算法的模板是void ,参数,终止条件,for循环横向遍历,递归纵向遍历。回溯算法的题都可以抽象为树,求解的过程就是看每一个节点已取了什么,还剩下些什么。 组合问题一般需要start作为标识(除了多个集合间的组合),排列问题不需要start,所以需要树枝去重,排列问题的树层去重尤其需要注意,因为其还需要判断是否处于同一层,分割问题就是对分割的位置进行回溯,棋盘问题也是对棋盘的选择进行回溯,比较重要的是判断是否冲突。原创 2022-09-14 10:52:58 · 185 阅读 · 0 评论 -
15.三数之和与18.四数之和
首先在不考率去重的情况下怎么做呢,当然可以直接暴力求解,时间复杂度为O(N^3),如果有三指针的话时间复杂度会降低到O(N^2),如何用三指针呢,其实就是先排序,然后第一个指针从左往右遍历,每次遍历先固定好第一个指针,然后另设两个指针,一个指向末尾,一个指向第一个指针的下一个元素,然后这两个指针依次往中间逼近看能否得到满足要求的三个数,逼近的方法是当前三个指针指向元素的和若大于0则末尾指针左移,若小于0则左边的那个指针右移(因为排了序的所以这样移动可以往0逼近)。......原创 2022-08-01 10:13:48 · 126 阅读 · 0 评论 -
15.三数之和
首先在不考率去重的情况下怎么做呢,当然可以直接暴力求解,时间复杂度为O(N^3),如果有三指针的话时间复杂度会降低到O(N^2),如何用三指针呢,其实就是先排序,然后第一个指针从左往右遍历,每次遍历先固定好第一个指针,然后另设两个指针,一个指向末尾,一个指向第一个指针的下一个元素,然后这两个指针依次往中间逼近看能否得到满足要求的三个数,逼近的方法是当前三个指针指向元素的和若大于0则末尾指针左移,若小于0则左边的那个指针右移(因为排了序的所以这样移动可以往0逼近)。...原创 2022-08-01 09:53:50 · 100 阅读 · 0 评论 -
142.环形链表Ⅱ
在相遇时慢指针走过的路程为a+b(为什么直接在第一圈相遇待会再解释),快指针走过的路程为a+n*(b+c)+b,又因为快指针走过的路程是慢指针的两倍,所以2*(a+b)=a+n*(b+c)+b,化简可得a=n*(b+c)-b,再化可得a=(n-1)*(b+c)+c,n是大于等于1的(相遇时快指针至少走了一圈),所以这时头结点到环形入口的距离是相遇的结点到环形入口的距离加上整数倍的环形周长。直接看双指针怎么做。...原创 2022-07-31 10:14:37 · 48 阅读 · 0 评论 -
135.分发糖果和406.根据身高重建队列
这道题最主要的就是要和左右两个孩子比较,但遍历到每个孩子时同时比较左右两边会顾此失彼,例如当自己提高后,左边比自己评分高的孩子要不要提高糖果数,再左边的呢,比较麻烦,写倒也可以写,不过我写了个发现不符合时间要求,对于这种要同时从两个维度考虑问题的题,我们可以先确定一个维度,再去确定另一个维度,我们可以先从左至右只比较左边孩子和当前孩子,再从右至只左比较当前孩子和右边孩子,这样时间复杂度降下来了,也比较容易理解。406. 根据身高重建队列这个题和上面的题类似,也是同时要从两个维度考虑问题,我们可以先考虑一原创 2022-07-09 09:05:12 · 44 阅读 · 0 评论 -
376.摆动序列
这个题既可以用贪心做,也可以用动态规划做。要使用贪心算法最关键的条件是局部最优能够推出整体最优,即小局部的最优可以推出大局部的最优,局部慢慢变大就成了整体,最后推出整体最优,这里很明显整体和局部的构造是相同的,所以可以用贪心算法,那怎么找局部最优呢,这道题就是给一个局部,然后把其中不符合条件的直接删除即可,我们可以整体序列看作这样的一个图:图片来源:代码随想录局部最优就是删除那些连续向上或向下的点,所以我们只需要判断当前点是否为连续点还是波峰或波谷,若是后两者,则摆动序列最大长度加1,注意最后一个节点我们没原创 2022-07-08 10:53:41 · 134 阅读 · 0 评论 -
回溯法总结
回溯的核心在横向展开以及纵向递归,使用回溯相当于暴力求解,虽然可以利用剪枝进行一些优化,但是效率还是不会很高。一般来说回溯问题可看作一棵树,树的每个节点时一个集合,树的横向展开即对集合中值不同的取法,纵向的递归就是一连串的取值导致的结果,最后这一连串的取值构成我们想要的结果,中途可以剪枝提高效率。递归后要修改某些变量的值达到回溯的效果。例如:这题每个节点的集合就是一个整数数组,然后取值是去取集合各个位置不同的值以横向展开,然后纵向展开即取完一个值后集合剩余的元素继续回溯。最后路径上的值的和若等于目标值则可以原创 2022-06-26 15:29:51 · 100 阅读 · 0 评论 -
77.组合
这道题当然可以用for循环嵌套解决,但是嵌套层数要根据k变化,很难写,所以还是用回溯。回溯的核心就是for循环横向展开,然后递归纵向展开,例如n=4, k=2,一开始的集合是[1,2,3,4],然后for循环依次取1,2,3,4,取了之后就递归继续分解。图片来源:代码随想录。回溯回来的时候一定要消除递归的影响,即把改变了的值改回来。剪枝优化:回溯一般可以剪枝,剪枝在递归的前面进行,判断要不要进入递归,这里的剪枝方法是判断剩下的数字够不够用了。k-path.size()是还需要的数字个数,n-i+1是仍有原创 2022-06-26 12:52:18 · 71 阅读 · 0 评论 -
二叉树递归总结
我们在做关于树的问题时常常用到递归,关于递归,我觉得最核心的一点就是递归的单层逻辑的目的,即递归的代码从本质来说要完成什么。并且在构建单层逻辑和使用的时候记住这个目的。知道了单层逻辑,则返回值,参数,截止条件这些也就不难写了。例如下面这题:669. 修剪二叉搜索树这一题很明显可以用递归,那么递归的单层逻辑是什么呢,就是使当前递归到的结点成为一个合格的结点。在使用的时候要记住这个目的。在构建的时候要细化这个目的。构建与使用又是一体的。目的是合格的结点,什么叫合格呢?即当前节点的值满足要求,并且其左右结点(子树原创 2022-06-25 19:41:48 · 99 阅读 · 0 评论 -
二叉树的遍历总结
二叉树的遍历分为前序遍历、中序遍历、后续遍历和层次遍历,前三种可看作是深度优先遍历,层次遍历可看作广度优先遍历,先介绍前三种。前中后的区分主要在于什么时候处理根节点,若是根左右(或叫中左右)根最前遍历则为前序遍历,依次类推,左根右为中序遍历,左右根为后序遍历。根左右的意思是对于每一个子树,先遍历根节点,再访问左子节点,再访问右子节点,注意是对每一个子树。例如对树:这三种遍历来说递归实现是比较好理解的。写递归,主要就三点:例如对于前序遍历:这种递归比较好理解,其他两种遍历方式更改一下顺序就行。迭代写法又有原创 2022-06-06 13:39:55 · 931 阅读 · 0 评论 -
前k个大数问题总结
所谓前k个大数问题,一般是指求第k个大的元素,或者出现频率第k高的元素,这种题一般用优先队列比较好解决,有时候需要结合哈希表先把元素存储下来,可能还要重写优先队列排序函数。其实这里代码写的还不完善,可以使用一个最小堆,这样时间复杂度可以控制在O(logn * k),使用最大堆需要对所有元素进行排序。这个就比较简单,直接用优先队列就行。692. 前K个高频单词这题倒和第一题差不多,不过有一个坑的地方,就是题目说如果不同单词频率相同,则按字典序排序,意思是如果频率相同,字典序小的排在前面,而不是大的排在前原创 2022-06-02 13:07:19 · 160 阅读 · 0 评论 -
单调区间之239.滑动窗口最大值
这个题如果用普通的优先队列是有问题的,因为每次弹出去的不知道是上一个窗口的左边界还是这个窗口里的元素。我们要的是每次只弹出上一个窗口的左边界并且还能弹出最值,所以这道题用的是单调队列,单调队列就适合求这种动态区间的最值问题。所谓单调队列,队头队尾都可以弹出,只有队尾可以压入,每次压入数据时,数据与当前队尾元素进行比较,若满足条件则弹出当前队尾元素,循环比较直至不满足条件,此时才将数据压入队尾。为什么单调队列适合求动态区间的最值问题呢,因为将区间新加入的元素压入时,可以保证压入剩余的元素都是在新加入元素左边且原创 2022-06-02 13:04:47 · 64 阅读 · 0 评论 -
KMP算法总结
KMP算法(一)(对比暴力匹配算法) KMP算法(研究总结,字符串) KMP算法的作用是在A串中查找是否有B串的算法,时间复杂度为O(n+m)。原理是利用上次匹配的信息,这次匹配并不是从上一次A串中开始匹配的下一个位置开始,而是直接从上一次匹配的不匹配位置开始,且不是从头匹配B串,而是匹配B串中还未匹原创 2022-06-01 13:36:23 · 575 阅读 · 0 评论 -
几数之和总结
1.两数之和1. 两数之和这个题应该算是几数之和里面最简单的问题了,就直接用unordered_map就行。class Solution {public: vector<int> twoSum(vector<int>& nums, int target) { vector<int>temp(2,-1); unordered_map<int, int> mymap; for(int i =原创 2022-05-31 11:31:48 · 109 阅读 · 0 评论 -
字母异位词总结
242. 有效的字母异位词这个题应该算是很简单的,也可以不用unordered_map这种,这种键值比较少的可以直接用数组。/*** 解题思路:使用unordered_map,键值为字母,实值为出现的次数,用变量sum保存map中现有总数量,首先将s* 的字母放进哈希表中,同时更新sum,然后依次判断t中字符是否在map中,若不在,直接返回false,若在,则使* 对应实值减1,若减为0,从哈希表中删去,同时sum减1.*/class Solution {public: bool原创 2022-05-31 11:06:11 · 390 阅读 · 0 评论 -
24.两两交换链表中的节点
24. 两两交换链表中的节点这个题呢,说难也不难,没什么典型算法,就自己想,我想的有点麻烦,因为是一个个移的,所以要判断在当前这个节点要不要交换,还是像官方题解那样做比较简单,就两个结点整体的跳,还是有一个prev指针,这样会好看简洁点。下面是我的代码/** * 解题思路:如果只有一个节点,返回头结点;先把前两个节点给交换了,把头结点重新设置,然后设立三个变量: * beforePrev,prev,cur,beforePrev初始为现在的第二个节点,prev为空,cur为第三个节点,设定变量i来原创 2022-05-20 19:56:50 · 230 阅读 · 0 评论 -
206.反转链表
206. 反转链表这题也不难,就是我被有个地方坑住了,就是最初的prev我还单独开了一块空间并且赋值了,后来我发现直接赋值为nullptr不就好了吗。还有递归,可能是经验不足,一直在想怎么用题目给的函数(只有一个参数)来完成,结果后面发现是自己再单独写函数,向右多少个参数都行。具体的逻辑就是在进入下一节迭代或递归前传入当前值和上一次的值(即cur和cur-next)。然后每一次把当前值的next变为prev即可。迭代的方法:/** * 迭代的方法:设立四个变量,一个代表当前节点cur,一个代表原创 2022-05-20 19:55:57 · 156 阅读 · 0 评论 -
707.设计链表
707. 设计链表题目中这个“假设链表中的所有结点都是0-index”的意思应该就是指索引从0开始。设计链表本身难度不高,就是可能各种地方会出点错,反正你要保持链表的定义不变,例如我用的链表的定义就是有一个空的头结点,索引从0开始,空头结点不加入长度的计算,每次查找或其他操作的索引也是从0开始计算的。struct NewListNode { int val; NewListNode* next; NewListNode() : val(0), next(nullptr) {}原创 2022-05-20 19:55:12 · 212 阅读 · 0 评论 -
203.移除链表元素
203. 移除链表元素[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RxpT3HGt-1653047640837)(C:\Users\q1369\AppData\Roaming\Typora\typora-user-images\image-20220520143424285.png)]这个题还是比较简单,直接迭代循环做就行,虽然官方还给了一个递归的题解,但是我觉得没必要。唯一有技巧的点可能在于加一个空的头结点,其实链表加一个空的头结点都会好做一些。/** * Def原创 2022-05-20 19:54:20 · 68 阅读 · 0 评论 -
螺旋矩阵总结
对于螺旋矩阵来说,没有什么很出名的算法来特定解决这个问题,不过对于这种题的解题方法和套路也很多,这里介绍两种:一种是以方向为主,每次根据正在进行的前进方向做讨论,然后每个方向又有遇到转角的情况,这个方法思维还是很清晰的,而且时间复杂度不会比其他的方法高,也不是很麻烦。一种是以边界为主,即每次沿着边界前进,每一次大循环都把整个边界走一遍,每次走完一个方向的边界就把相应边界缩小,这个方法优点就是代码量少,看起来很简介,不过有一个问题,就是判断什么时候结束,因为可能存在在中间走完的情况,就有可能会多压几个数据原创 2022-05-19 18:18:12 · 2277 阅读 · 0 评论 -
滑动窗口法总结
滑动窗口法用于求某个序列满足特定条件的连续子序列。优点是时间复杂度低。具体使用方法为设立左右边界(具体是左闭右开还左闭右闭都行,不过要保持区间的开闭性不变),边界内是子序列,然后根据是否满足条件决定左右边界的移动,一般来说,求最长子序列时,满足条件右边界移动,不满足条件左边界移动,求最短子序列时,满足条件左边界移动,不满足条件右边界移动。有时候最关键的是怎么判断条件满足,一般我们可以用一个总量式的变量来提高判断效率。还有一个比较麻烦的点在于左右边界移动时加入删除某些元素引起的关键条件的变化。下面看题。原创 2022-05-18 20:07:05 · 873 阅读 · 0 评论 -
1.两数之和
两数之和这道题可以用map或者unordered_map来做。解题方法:把数组中所有元素放入一个哈希表中,键值为元素值,实值为元素序号,然后使用一个循环从头遍历数组,对目标值减数组中每个元素值得到的值在哈希表中查找,若找到,则输出当前元素值的序号和哈希表中相应的实值。上面的是最普通的解题方法,也可以边放入哈希表中边查找,反正一开始不在哈希表中的数在对后面的数进行查找时如果满足条件也会找到前面的数。(即这个数组其实从正面反面找都能找到答案的)。class Solution {public:原创 2022-05-16 13:49:36 · 53 阅读 · 0 评论 -
2.两数相加
两数相加/** * 解题思路:定义结果链表头结点,定义进位标志(0),开始循环,定义中间变量节点, * 若两个链表均不为空,则两个链表对应元素相加再加上进位标志若大于10则取余并将进位标志置1,否则置0,将得到 * 结果赋予中间变量节点并加入结果链表中,若只有一个链表为空,则将不为空的那个链表元素加上进位标志若大于10则结果 * 取0进位标志置为1,否则进位标志置为0,结果赋予中间变量并加入结果链表中,当两个链表均为空时结束循环,结束循环后 * 判断进位标志是否为1,若为1则生成新节点值为1并原创 2022-05-16 13:50:31 · 69 阅读 · 0 评论 -
3.无重复字符的最长子串
无重复字符的最长子串题解:/** * 解题思路:定义变量头部序号(0)和滑动窗口(0),定义一个unordered_set,定义下一个要加入滑动窗口的元素位置(0), * 定义长度变量为字符串的长度。定义一个当前窗口大小(0),开始循环 * 将下一个要加入滑动窗口的元素在哈希表中查找,若未找到,则将其加入哈希表中,元素值为键值, * 同时当前滑动窗口大小加1.用当前滑动窗口大小更新滑动窗口大小,下一个要插入的位置加1. * 若找到,则将头部序号对应的值从哈希表中删除,头部序号加1,当前窗口大原创 2022-05-16 13:51:21 · 38 阅读 · 0 评论 -
二分查找总结
所谓二分查找,就是对区间进行分解从而缩小区间范围进而找到所需值,二分查找的条件是有序,优点是一般时间复杂度为O(logn).下面看题:第一题二分查找的难点在于哪里,就在于while(left<right)还是while(left<=right)还有right = middle还是right = middle - 1。这两者怎么区分呢,其实主要就是看你的区间的定义,看你定义的区间是左闭右闭的还是左闭右开的(按道理其他也行,但是一般不用)。如果区间定义为左闭右闭,那么就是while(lef原创 2022-05-16 21:23:05 · 155 阅读 · 0 评论 -
双指针法总结
双指针法在对数组做出某种改变中经常用到,优点是时间复杂度为O(n),且一般空间复杂度也比较低。所谓双指针法,就是利用两个指针(不一定真是指针,能存储相应元素的位置就行)分别标识两个位置,然后通过指针所指元素的性质对数组进行修改,同时指针进行移动完成目标的方法。一般双指针有两种使用方法,一是分为快慢指针,慢指针指向下一个要填充的位置,快指针指向下一个要判断的位置,若快指针指向的元素符合相应的条件,则把元素填充(或交换)到慢指针所指位置,然后两个指针进行移动。这样还可以保持数组的相对顺序(如果不用保持相对顺原创 2022-05-17 19:30:32 · 1599 阅读 · 0 评论