![](https://img-blog.csdnimg.cn/20201014180756922.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
算法随想
文章平均质量分 71
刷题过程中的一些总结
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 评论 -
并查集总结
并查集是一种树型的数据结构,用于处理(不相交)集合的合并、查询及由此衍生的问题。其功能主要有三个: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 评论 -
并查集总结
关于并查集的一些知识原创 2022-07-23 11:05:34 · 222 阅读 · 0 评论 -
单调栈总结
单调栈一般用于求任一元素左侧或(和)右侧第一个大于或小于的元素位置。特别要注意的是一般栈中存储的是元素的下标,所以使用的时候要转化一下。原创 2022-07-16 15:59:35 · 291 阅读 · 0 评论 -
动态规划中的子序列问题
这题主要是要想清楚dp[i]的含义,即dp[i]表示以nums[i]结尾的最长递增子序列的长度。所以我们每次都要把nums[i]和前面的每个dp[i]比较来得到当前最大的长度。即转移方程为由此代码就比较好写了。674. 最长连续递增序列这题就比较简单了吧,特别在上一题的基础上,这个要求连续,所以每次只需要和前面一个比较即可,也可以用贪心来做,这样空间复杂度会降为O(1).718. 最长重复子数组这个题说难也不难,反正dp数组的定义要落在以某个数结尾上来,这里dp[i] [j]表示以nums1的第i原创 2022-07-14 16:12:42 · 144 阅读 · 0 评论 -
买卖股票的最佳时机问题总结
买卖股票问题是经典的动态规划问题(有时候贪心也能解)。 dp[1] = max(dp[1], -prices[i]),dp[1]可能由前一天持有股票或者今天才买入股票得出122. 买卖股票的最佳时机 II这题和上面那道题差不多,只是现在你可以多次买入多次卖出了,dp数组定义不变,dp[1]的递推方程要变一点,之前只能买入一次,所以在买入前收益肯定是0,所以dp[1] = max(dp[1], -prices[i]),现在买入前收益不一定是0了,可能是有收益的,所以原创 2022-07-13 17:02:22 · 70 阅读 · 0 评论 -
打家劫舍问题总结
打家劫舍问题是经典的动态问题第一道比较简单,dp[i]表示偷到第i家的时候最大收益,有两种选择,一种是偷第i家一种是不偷,偷的话收益为dp[i - 2] + value[i],即偷第i - 2家的最大收益加上第i家的收益,不偷的收益为dp[i - 1],这个好理解吧。所以递推公式为:从递推公式也可以看出了要初始化dp[0]和dp[1]。最终代码为:还有一个更简易的版本,即只考虑每家偷不偷,偷的收益记录下来,不偷的收益记录下来,代码为:这种写法空间复杂度降为O(1)。这道题如果会做上面那道题也比较简单,原创 2022-07-13 14:36:24 · 127 阅读 · 0 评论 -
完全背包问题总结
完全背包问题与01背包问题有很密切的关系,关于01背包可以看这篇文章:01背包问题总结我这里面写的代码将主要采用上面文章提到的一维dp的方法。完全背包是指有一个有固定容量的背包,装一些物品使得背包中的价值和最大,每种物品有无数个,应如何装。与01背包的区别就在于完全背包的每种物品数量是无限的,可以重复装某一物品。所以每次装某种物品时,需要考虑装0个,1个,2个…该物品的情况,直到装不下。代码为:如果用一维数组来写的话应该为这样:这种写法可见上面的文章,注意这里与01背包的一维写法不同的是容量是由大到小遍原创 2022-07-13 09:58:21 · 180 阅读 · 0 评论 -
01背包问题总结
背包问题是经典的动态规划问题,而01背包基本算得上是其他背包问题的基础,01背包问题即有一个有固定容量的背包,用这个背包去装有固定大小和固定价值的一些物品,问怎么装(装哪些物品)能让背包内价值总和最大。因为每个物品要么装要么不装,对应1和0,所以又叫01背包问题。首先01背包可以用最普通的动态规划解决,首先dp[i] [j]代表在容量为j的情况下,在0~i个物品中选择使得背包价值最大。对于第i个物品,我们可以选择装或者不装,主要是判断这两种情况哪种使得背包价值最大,即然后是初始化,如果nums = {1,原创 2022-07-11 11:14:50 · 875 阅读 · 0 评论 -
回溯法总结
回溯的核心在横向展开以及纵向递归,使用回溯相当于暴力求解,虽然可以利用剪枝进行一些优化,但是效率还是不会很高。一般来说回溯问题可看作一棵树,树的每个节点时一个集合,树的横向展开即对集合中值不同的取法,纵向的递归就是一连串的取值导致的结果,最后这一连串的取值构成我们想要的结果,中途可以剪枝提高效率。递归后要修改某些变量的值达到回溯的效果。例如:这题每个节点的集合就是一个整数数组,然后取值是去取集合各个位置不同的值以横向展开,然后纵向展开即取完一个值后集合剩余的元素继续回溯。最后路径上的值的和若等于目标值则可以原创 2022-06-26 15:29:51 · 100 阅读 · 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 评论 -
螺旋矩阵总结
对于螺旋矩阵来说,没有什么很出名的算法来特定解决这个问题,不过对于这种题的解题方法和套路也很多,这里介绍两种:一种是以方向为主,每次根据正在进行的前进方向做讨论,然后每个方向又有遇到转角的情况,这个方法思维还是很清晰的,而且时间复杂度不会比其他的方法高,也不是很麻烦。一种是以边界为主,即每次沿着边界前进,每一次大循环都把整个边界走一遍,每次走完一个方向的边界就把相应边界缩小,这个方法优点就是代码量少,看起来很简介,不过有一个问题,就是判断什么时候结束,因为可能存在在中间走完的情况,就有可能会多压几个数据原创 2022-05-19 18:18:12 · 2277 阅读 · 0 评论 -
滑动窗口法总结
滑动窗口法用于求某个序列满足特定条件的连续子序列。优点是时间复杂度低。具体使用方法为设立左右边界(具体是左闭右开还左闭右闭都行,不过要保持区间的开闭性不变),边界内是子序列,然后根据是否满足条件决定左右边界的移动,一般来说,求最长子序列时,满足条件右边界移动,不满足条件左边界移动,求最短子序列时,满足条件左边界移动,不满足条件右边界移动。有时候最关键的是怎么判断条件满足,一般我们可以用一个总量式的变量来提高判断效率。还有一个比较麻烦的点在于左右边界移动时加入删除某些元素引起的关键条件的变化。下面看题。原创 2022-05-18 20:07:05 · 873 阅读 · 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 评论