![](https://img-blog.csdnimg.cn/944579ce6b6443f9ad2bfe65583dd568.png?x-oss-process=image/resize,m_fixed,h_224,w_224)
剑指offer
文章平均质量分 55
刷题
bugcoder-9905
长期更新学习笔记
展开
-
二分查找左右边界
【代码】二分查找左右边界。原创 2023-05-08 21:37:18 · 221 阅读 · 1 评论 -
字典树用法
int minimumLengthEncoding(vector<string>& words) { int len = words.size(); vector<string> r_words; for(string word : words){ reverse(word.begin(), ...原创 2020-04-24 20:07:24 · 411 阅读 · 1 评论 -
287. 寻找重复数
快慢指针思想,fast 和 slow 是指针,nums[slow] 表示取指针对应的元素,注意 nums 数组中的数字都是在 1 到 n 之间的(在数组中进行游走不会越界),因为有重复数字的出现,所以这个游走必然是成环的,环的入口就是重复的元素。原创 2023-05-02 15:12:33 · 121 阅读 · 0 评论 -
【拓扑排序】课程表系列
比如上图中,根据dfs,从0出发后会遍历到1、3,然后会遍历2(visited[2]=true、path[2]=true),遍历3(visited[3]=true、path[3]=true),遍历4(visited[4]=true、path[4]=true),遍历2,然而此时发现path[2]=true,出现环!开始执行 BFS ,不断弹出队列中的节点(表示修完一门课),减少相邻节点的入度(表示先修课程 - 1),并将入度变为 0 的节点加入队列。后序遍历反转就得到结果。原创 2023-04-29 22:13:50 · 1111 阅读 · 0 评论 -
【dp】最长递增子序列
min_tails数组中维护各个不同长度递增子序列末尾元素的最小值时,arr的后续元素可以和不同长度子序列末尾的最小值比较,从而确定后续元素可以加入哪个子序列,成为新的递增子序列。看最后一个g数组,g[2]=3,表示长度为3的递增子序列末尾的最小值为3。长度为3的递增子序列有[1,6,7]、[1,2,4]、[1,2,5]、[1,2,3]从0遍历到i - 1,若遍历到的元素小于当前值arr[i],表示当前值arr[i]可以和前面的某个值组成递增序列,则尝试更新dp[i]原创 2023-04-23 20:45:26 · 478 阅读 · 0 评论 -
【dp】最长回文子串 & 最长回文子序列
【代码】【dp】最长回文子串 & 长回文子序列。原创 2023-04-07 20:50:10 · 133 阅读 · 0 评论 -
【dp】最大子数组和&乘积最大子数组
dp[i]:用到了nums[i]连续子数组的最大和用到了nums[i]连续子数组的最大和dp[i],要么是前面用到了nums[i - 1]的连续子数组最大和dp[i - 1],要么就是nums[i]152. 乘积最大子数组数组的动态规划问题、连续子序列的一个常见的状态定义是:以下标 i 结尾的连续子序列的乘积的最大值最后把整个 dp 数组看一遍求最大值即可。因此状态转移方程可能是:dp[i] = max(dp[i - 1] * nums[i], nums[i])说明:牢记状态的定义,一定以下标 i 结尾原创 2023-03-30 20:49:52 · 290 阅读 · 1 评论 -
【模拟】V字形打印字符
【代码】【模拟】V字形打印字符。原创 2023-03-14 16:49:05 · 189 阅读 · 0 评论 -
【位运算】只出现一次的数字系列
由于a、b两个数字不一样,所以异或的结果不为0,我们找到res的二进制表示中第一个为1的bit(这个bit表示a、b对应位置的bit异或结果位1,即a、b该位置的bit不同),以这个bit是否为1,将整个数组分为两部分,这样相同的数肯定分在两个数组中,而a、b肯定不在一个数组中。由于异或运算满足交换律,且相同的数异或为0,任何数和0异或不变,所以整个数组逐元素异或的结果res就是两个只出现一次的a、b相异或的结果res。数组中所有元素都出现2次,求出只出现一次的数字,整个数组逐元素相异或即可。原创 2023-03-16 10:21:15 · 370 阅读 · 0 评论 -
【回溯】排列树和子集树
如果纵向增长时,传入fun(startIdx + 1),会导致每层增长的结果都一样。下一层横向增长的时,需要往i之后增长,而不是往startIdx后增长。for循环从startIdx开始,控制横向增长。求子集时,每个节点都要收集path。递归调用,用i + 1控制纵向增长。原创 2023-03-19 11:36:52 · 157 阅读 · 0 评论 -
【二分查找】旋转数组的二分
用mid将整个区间分为[l, mid]和[mid+1, r],当nums[mid] >= nums[l]时,则[l, mid]有序。当nums[mid] < nums[l]时,则[mid+1,r]有序。将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。先判断target是否存在于有序区间中,若存在,则调整l、r,继续在有序区间查找。若target不在有序区间,则调整l、r去另一半部分有序的区间查找。原创 2023-03-20 10:58:31 · 347 阅读 · 0 评论 -
【区间dp】戳气球
以两个数作为左右端点,找出最优解中它们中间那个戳破的气球,中间这个气球把整个队列分为了2部分,要想让中间这个气球和2个端点靠在一起,就需要先把分开的2部分的气球戳破。dp[i][j]表示i~j最大值,i,j不戳破。比如k气球在i,j之间时(i,k,j)被戳破,那么要先戳破i,k、k,j之间的气球,所以。原创 2023-03-12 13:57:01 · 131 阅读 · 0 评论 -
【LeetCode】不同的二叉搜索树 II
二叉搜索树关键的性质是根节点的值大于左子树所有节点的值,小于右子树所有节点的值,且左子树和右子树也同样为二叉搜索树。而左子树和右子树的生成相较于原问题是一个序列长度缩小的子问题,我们可以通过控制区间大小,使用递归函数完成。个,两个for遍历每种情况,即可生成以i为根节点的BST序列;,说明当前区间不能生成BST树,返回。中每个结点都能作为根节点生成左右子树。为根结点的BST(子)树有。然后以for循环使得。原创 2023-03-05 09:38:32 · 164 阅读 · 0 评论 -
【LeetCode】背包问题总结
public :// 01背包:逆序遍历 // 组合问题:先物品、后容量 int lastStoneWeightII(vector < int > & stones) {// dp[j]:容量为j的背包,最多装dp[j]的石头 for(int i = 0;i < m;dp背包问题——1049. 最后一块石头的重量 II。原创 2023-03-02 15:06:21 · 1341 阅读 · 0 评论 -
【LeetCode】字符串相乘
【代码】【LeetCode】字符串相乘。原创 2023-02-24 21:36:50 · 137 阅读 · 0 评论 -
【前缀和】和为k的子数组 & 路径总和 III
假设当前sum表示[0,9]区间的和,前面[0,3]、[0,5]、[0,7]区间的和是sum - k,则说明[4,9]、[6,9]、[8,9]三个区间的和为k,此时mp[sum - k]应该是3。,key表示和的值,value表示和出现的次数。哈希表存放的key表示区间[0,0]、[0,1]、[0,2]、[0,3]、[0,4]…比如nums = {1,2,3},则map中最终存放的是{{0,1},{1,1},{3,2},{6,1}}构建前缀和数组,以快速计算区间和;动态规划算法(超时)原创 2023-02-22 15:19:26 · 341 阅读 · 0 评论 -
【dp】LeetCode打家劫舍系列
题目描述这题一看就知道得用动态规划做,本来在学校是学过的,但缺少实践,理论知识也忘得差不多了,看到这题还是不知道该怎么用dp解,只能翻看题解,理解后独立写出代码dp方程如下:dp[i] = max(dp[i-2] + nums[i] , dp[i-1])dp[i]表示偷了 i 家时,获得的最大收益nums[i]表示当前准备偷的房屋有多少钱理解下来就是:偷了 i 家后,我所获得的最大...原创 2020-03-30 13:20:54 · 267 阅读 · 0 评论 -
【DFS&并查集】岛屿数量
从没有访问过的某个陆地出发,将所有能到达的陆地的状态都记录为已访问。下次出发不从已访问的陆地出发,每次出发前都把岛屿数 + 1即可。遍历二维数组,合并不同的坐标时,在每个陆地坐标处查看自己右侧和下方的位置是否是陆地,如果是,合并当前陆地和自己右侧或下方的陆地。经典的dfs/bfs问题,给一个起点开始搜索,满足条件则继续调用dfs/bfs。遍历完成后,并查集构造完成,直接获取并查集的集合数即可。原创 2023-02-11 17:05:35 · 165 阅读 · 0 评论 -
【哈希表&并查集】最长连续序列
先用哈希表去重,遍历哈希表中的元素num,如果num - 1不在哈希表中,说明num可以作为连续序列的左边界,然后不断查看num + 1,num + 2,…是否在哈希表中,知道找到右边界,更新以num为左边界的连续序列的长度。遍历所有元素num,如果num+1存在于并查集中,连接num加入到num+1所在的连通分量,并将num+1所在集合的根节点作为父节点。重新遍历一遍所有元素num,通过find函数找到num所在分量的根结点,也就是最远右边界,从而求得连续区间的长度。原创 2023-02-10 17:12:36 · 158 阅读 · 0 评论 -
【dp】买卖股票的最佳时机系列题目
第i天不持有股票所得最多现金 = max(第i-1天不持有股票手上的现金, 第i-1天手上持有股票、但是在第i天卖出手上的现金)第i天持有股票手上的现金 = max(第i-1天持有股票手上的现金, 第i-1天手上没有股票、但是在第i天买入股票手上的现金)第i天持有股票手上的现金 = max(第i-1天持有股票手上的现金, 第i-1天手上没有股票、但是在第i天买入股票手上的现金)第i天不持有股票,且不在冷冻期能获得的最大收益 = max(前一天处于冷冻期的收益,前一天不持有股票且不在冷冻期的收益)原创 2023-01-12 18:36:05 · 787 阅读 · 0 评论 -
【dp】排列问题——零钱兑换和组合总和IV
如果先遍历物品,后遍历容量的话,举一个例子:计算dp[3]的时候,结果集只有 {1,2} 这样的集合,不会有{2,1}这样的集合,因为nums遍历放在外层,3只能出现在1后面!很明显,本题使用完全背包算法,求解的是组合数,直接使用完全背包算法即可,为什么是组合数呢?其实就是说7的组合数可以由三部分组成,1和dp[3]、2和dp[2]、3和dp[1]dp[i]: 使用数组nums,凑成整数 i 的排列个数。举个例子比如nums=[1,2,3],target=4。为什么不能先遍历物品,后遍历容量呢?原创 2023-01-09 19:04:05 · 522 阅读 · 0 评论 -
【并查集】冗余连接
无入度为2的节点,但是存在有向环,表示删除环中哪条边都可以,从前往后在图中加入边,一旦成环就返回,这样可以返回出现在edges中靠后的边。我们先找出入度为2的节点,并找到对应的边(一定是两个),遍历所有的边,看不使用这两条边的哪一个可以构成树,就返回哪条边。题目中的图原本是是一棵树,在不增加节点的情况下多加了一条边。在树中所有节点的入度都是1,如果有环则存在两种情况。删除一条边成为树,不成环就是树了,如果发现合并当前边的两个节点,在同一集合中,则应该删除这条边。原创 2022-12-29 12:58:42 · 396 阅读 · 0 评论 -
【dp】最长递增子序列和最长递增子序列的个数
需要遍历dp[0 ~ i-1],才能求出dp[i],如果前面某个dp[j] + 1大于当前dp[i],则说明构成当前最长子序列dp[i],可能用到nums[j],更新dp[i]的同时更新cnt[i],直接用cnt[j]赋值。当前dp[i]计算出来后,就可以查看0 ~ i-1元素中哪些元素比nums[i]小,且最长子序列长度dp[j]比当前最长子序列长度dp[i]小1,说明当前最长子序列长度用到了j号元素,cnt[i] += cnt[j]原创 2022-12-27 22:37:03 · 106 阅读 · 0 评论 -
【单调栈】柱状图中最大的矩形
本题是要找每个柱子左右两边第一个小于该柱子的下标,所以从栈底到栈顶的顺序应该是从小到大的顺序,这样栈顶的两个元素和即将入栈的元素就能形成小-大-小,这样就能找到左右两边第一个小于当前柱子的下标。,而不是第一个小于该柱子的高度。然后使用两个下标之间的差作为矩形的底(底不包括两个下标所在位置),当前柱子的高度作为矩形的高,计算当前矩形面积。原创 2022-12-18 22:17:36 · 181 阅读 · 0 评论 -
【单调栈】接雨水
接雨水时,以当前元素height[i]为右侧,当前栈顶元素height[mid]为底,出栈后的栈顶元素为左侧height[st.top()],形成的凹槽接雨水。例如当前元素height[i]为5时,当前元素是一直大于栈顶元素的,需要不断计算以不同的栈顶元素为底的容器接的雨水,直到栈顶元素不小于当前元素height[i]为止。单调栈是按照行方向来计算雨水,栈中按照栈底到栈顶,高度降序的方式存放对应的下标,一旦遍历到的元素height[i]大于栈顶元素了,说明就有凹槽,可以用来接雨水了。原创 2022-12-17 11:15:10 · 551 阅读 · 0 评论 -
【单调栈】每日温度、下一个更大的元素 I、下一个更大的元素 II
用单调栈处理nums2,将有下一个更大元素的值,和其对应的下一个更大元素用哈希表记录下来,遍历nums1,即可得到答案。通常是一维数组,要寻找任一个元素的右边或者左边,第一个比自己大或者小的元素的位置,此时我们就可以用单调栈。只有把栈中比当前元素小的元素全部出栈,才能把当前元素入栈,栈中元素一定是降序的(栈底->栈顶)将最大值第一次出现的位置前面所有元素搬到数组后面,后续按照单调栈相同思路解题。原创 2022-12-13 17:54:44 · 94 阅读 · 0 评论 -
【dp】LeetCode 392. 判断子序列
和相等,]是否是的子序列,取决于是否是的子序列和不相等,则是否是的子序列,取决于是否是的子序列方法二:判断s和t的最长公共子序列长度若s和t的最长公共子序列长度,就是s的长度,则说明s是t的子序列方法三:双指针指向的字符相同,同时往后移动。不相同,则把tp往后移动如果s是t的子序列,那最终sp一定指向原创 2022-12-07 18:06:19 · 127 阅读 · 0 评论 -
【数论】约数
比如a=24,b=18,那么gcd(24,18)=6,gcd(18,24%18)=6。,12的约数有1,2,3,4,6,12,约数之和为28,根据公式计算同样是。,12的约数有1,2,3,4,6,12共6个,根据公式计算同样是。约数和质因子并不是一个意思,但每个约数可以表示成质因子的乘积。我们分解质因子后,获取质因子的指数,最后套用。n的每个约数m都可以表示成。原创 2022-11-25 22:04:49 · 258 阅读 · 0 评论 -
【数论】质数
如果是合数,那么因数一定是成对出现的,比如12,有2*6=12,3*4=12,我们不用暴力枚举,从2一直枚举到n-1,我们只枚举成对出现的因数中较小的即可。素数的倍数一定不是素数,在用素数剔除时,一定会剔除合数。如果某个数p没有被删除,就意味着我们使用2~p-1没有把p删除,则p一定是质数。不是i的质因子,且因为当前的i是素数表里最大的素数,则。,这里的数n是不断除自己的因子,可以降低时间复杂度。,其中分母只有素数,根据素数定理,1~n中大概有。,调和级数,当n趋近于正无穷时,极限是。原创 2022-11-25 17:52:38 · 523 阅读 · 0 评论 -
dp完全背包问题解组合问题——零钱兑换
本题为完全背包问题,遍历容量需要顺序遍历。组合 —> 先遍历物品。排列 —> 先遍历容量。01背包 —> 逆序。完全背包 —> 顺序。原创 2022-11-23 21:48:23 · 286 阅读 · 0 评论 -
01背包、完全背包、多重背包、分组背包总结
我们现在需要根据(3)式的结果,推出(1)式的结果,(1)式的后s[i]项和(3)式的前s[i]项完全一样,但我们无法使用(3)式中s[i]+1项的最大值,推出(3)式的前s[i]项的最大值,所以无法使用完全背包问题的优化方式。一维形式中,如果逆序遍历容量,计算后面容量时,使用的就是没有计算过的结果,体现在二维中,使用的就是上一层的结果,这是不对的。,如果我们用k+1个新的物品,来代替多重背包的一个物品,使用01背包的方式计算出这k+1个物品选或不选。,如果说原来有n类物品,每类物品有s个,总共。原创 2022-11-23 16:37:11 · 844 阅读 · 1 评论 -
dp背包解决组合问题——494. 目标和
我们假设两组的和分别为a1、a2,数组的总和为sum,这样有a1+a2=sum,a1-a2=target(假设a1>a2),如果我们用01背包的方法解,问题就是:容量为a1的背包被装满,有多少种方法。i号物品,容量为j的背包装满的方法数dp[i][j],等于装物品nums[i]且装满的方法数dp[i-1][j-nums[i] + 不装物品nums[i]且装满的方法数dp[i-1][j]这就和之前遇到的背包问题不一样了,之前都是求容量为j的背包,最多能装多少,而这次是问装满有多少种方法。原创 2022-11-15 00:24:02 · 218 阅读 · 1 评论 -
dp背包问题——1049. 最后一块石头的重量 II
和416. 分割等和子集非常像,只需要使用石头总重量的一半作为背包容量,通过01背包的dp算法,本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。本题物品的重量为stones[i],物品的价值也为stones[i],就能使得最后剩余的石头最小。原创 2022-11-14 16:50:16 · 237 阅读 · 0 评论 -
dp背包问题——416. 分割等和子集
其实就是在数组中找一个子集,使得子集的和等于所有数和的一半,将和的一半记为target。我们用一个容量为target的背包,装入数组中的物品,如果刚好能装满,说明可以找到一个子集的和为target只有确定了如下四点,才能把01背包问题套到本题上来01背包的递推公式为:本题递推公式:滚动数组对于背包问题其实状态都是可以压缩的,在使用二维数组解01背包问题的时候,递推公式为:,计算当前层的值,只需要使用上一层的数据即可其实如果把 dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:,旧的就是上一层原创 2022-11-09 18:11:52 · 279 阅读 · 0 评论 -
dp—— 96. 不同的二叉搜索树
元素1为头结点搜索树的数量 = 右边有2个元素的搜索树数量 * 左边有0个元素的搜索树数量。元素2为头结点搜索树的数量 = 右边有1个元素的搜索树数量 * 左边有1个元素的搜索树数量。元素3为头结点搜索树的数量 = 右边有0个元素的搜索树数量 * 左边有2个元素的搜索树数量。上述三种情况加起来,就得到有三个节点的BST树的数量。有2个元素的搜索树数量就是dp[2]有1个元素的搜索树数量就是dp[1]有0个元素的搜索树数量就是dp[0]原创 2022-11-08 18:23:33 · 94 阅读 · 0 评论 -
LeetCode 376. 摆动序列(贪心)
上述代码构造ans数组时,diff1其实是不对的,nums[i]=2时,由于前一个也是下降,现在也是下降,按照题目意思这不属于摆动,所以我们不能只是查看前一个差,而应该记录前一个有变化的差。原创 2022-11-07 17:02:41 · 111 阅读 · 0 评论 -
回溯——LeetCode N皇后 解数独
我们已知一行只能有一个皇后,所以可以用一维数组chessboard表示皇后的位置,即(i, chessboard[i])表示第i个皇后的位置。通过上图全排列,剪枝的方式可以获得满足规则的皇后位置,验证是否冲突时,我们只需要验证和前面的皇后是否冲突即可,因为我们是一行一行放置皇后。原创 2022-11-02 19:37:59 · 703 阅读 · 0 评论 -
回溯——LeetCode 子集 子集 II 递增子序列
由于求子序列,不能对原数组排序,比如数组[4,6,7,6],如果排序,我们就会找出子序列[4,6,6,7],但题目要求我们在原数组中找出递增子序列,如果是在原数组中找递增子序列,就不应该出现[4,6,6,7],所以不能排序。排序以后,横向去重即可,横向去重时,值得注意的是,同一树层重复时,由于是求子集,不要求返回子集中数字的顺序,可以对原数组进行排序。中的方式进行去重,因为相同的元素不一样相邻,不能通过。进行去重,需要用哈希表记录当前树层使用过的元素。由于不能排序,我们不能使用。原创 2022-11-01 19:53:10 · 115 阅读 · 0 评论 -
回溯分割—— 93. 复原 IP 地址
无法剪枝,但不满足ip地址格式的情况为:已经分割完了,此时长度没达到4段,比如101.0.23,这种情况也不能添加到最后返回的vector。原创 2022-10-31 17:37:17 · 997 阅读 · 0 评论 -
回溯分割—— 131. 分割回文串
算法优化:先用dp算出所有子串是否是回文串,就不用频繁调用函数判断当前子串是否是回文串了。横向表示从某个下标开始不断截取不同长度的字符串,直到将字符串截取完。纵向是当前截取了当前字符串后,继续截取剩余字符串。从根节点到叶子节点的路径,就组成了分割结果。原创 2022-10-31 16:39:13 · 106 阅读 · 0 评论