- 博客(27)
- 收藏
- 关注
原创 【回溯 + 动态规划】LeetCode - 131. 分割回文串
题目描述题目链接解法1:回溯题目要求列出所有符合的情况,不满足回文的直接剪枝,很适合回溯。class Solution {private: vector<vector<string> > ans; vector<string> elem;//ans中的成员 int len; //检查是否是回文字符串 bool check(const string& str, int left, int right) {
2021-02-20 22:54:55 212
原创 【动态规划】LeetCode - 5. 最长回文子串
题目描述题目链接解法1. 定义状态dp[i][j] 表示子串 s[i…j] 是否为回文子串,这里子串 s[i…j] 定义为左闭右闭区间,可以取到 s[i] 和 s[j]。2. 状态转移方程dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1]根据定义可知,j 要严格大于 i(字符串长度为1的不考虑,本题要找的是最长字符串),dp[i][i] = true(即对角线)边界条件是:表达式 [i + 1, j - 1] 不构成区间,即长度严格小于 2,即
2021-02-20 22:44:53 209
原创 【回溯 + 背包 + 动态规划】LeetCode - 494. 目标和
题目描述题目链接解法1:暴力枚举 - 回溯/DFS(溢出)1. 回溯经常说回溯算法和递归算法有点类似,都涉及递归,有的问题如果实在想不出状态转移方程,尝试用回溯算法暴力解决也是一个聪明的策略,总比写不出来解法强。回溯算法其实是一个暴力枚举的算法,模板如下关键就是搞清楚什么是「选择列表」,而对于这道题,「选择列表」就是正号 + 或者负号 -,然后利用回溯模板穷举出来所有可能的结果,数一数到底有几种组合能够凑出 targetclass Solution {private: int
2021-02-20 11:20:35 287
原创 【DFS + 回溯 + 剪枝】LeetCode - 39. 组合总和
题目描述题目链接解法如何快速判断一道题是否应该使用 DFS + 回溯算法来爆搜。总的来说,你可以从两个方面来考虑:求的是所有的方案,而不是方案数。由于求的是所有方案,不可能有什么特别的优化,我们只能进行枚举。这时候可能的解法有动态规划、记忆化搜索、DFS + 回溯算法。通常数据范围不会太大,只有几十。如果是动态规划或是记忆化搜索的题的话,由于它们的特点在于低重复/不重复枚举,所以一般数据范围可以出到 10510^5105 到 10710^7107,而 DFS + 回溯的话,通常会限制在
2021-02-18 22:14:18 215
原创 【完全背包】LeetCode - 518. 零钱兑换 II
题目描述题目链接解法1. 确定dp数组以及下标的含义dp[j]:凑成总金额j的货币组合数2. 确定递推公式dp[j] 就是所有的dp[j - coins[i]](不考虑coins[i])相加。所以递推公式:dp[j] += dp[j - coins[i]];3. dp数组如何初始化首先dp[0]一定要为1,dp[0] = 1是 递归公式的基础。从dp[i]的含义上来讲就是,凑成总金额0的货币组合数为1。下标非0的dp[j]初始化为04. 确定遍历顺序如果求组合数就是外层for循环
2021-02-18 20:40:15 174
原创 【完全背包】LeetCode - 322. 零钱兑换
题目描述题目链接解法1:动态规划1. 确定dp数组以及下标的含义dp[j]:凑足总额为j所需钱币的最少个数2. 确定递推公式得到dp[j](考虑coins[i])只有一个来源,即 dp[j - coins[i]](没有考虑coins[i])。凑足总额为j - coins[i]的最少个数为dp[j - coins[i]],那么只需要加上一个钱币coins[i]即dp[j - coins[i]] + 1就是dp[j](考虑coins[i])所以dp[j] 要取所有 dp[j - coins[i
2021-02-18 20:05:12 198
原创 背包问题一般的套路模板
主要的模板转载自 这位大佬,为了方便以后查看,我把部分截图过来01背包二维写法一维写法完全背包二维写法一维写法完全背包 练习题LeetCode - 1449. 数位成本和为目标值的最大数字
2021-02-18 18:31:16 307
原创 【完全背包】LeetCode - 1449. 数位成本和为目标值的最大数字
题目描述题目链接解法01背包 vs 完全背包每件物品要么拿那么不拿(最多拿一次)是 01背包 问题,如果每件物品可以重复拿多次是 完全背包 问题,本题显然是完全背包问题恰好装满 VS 可以不装满题目有两种可能,一种是要求背包恰好装满,一种是可以不装满(只要不超过容量就行)。而本题是要求恰好装满的。而这两种情况仅仅影响我们dp数组初始化。恰好装满。只需要初始化dp[0] 为 0, 其他初始化为负数(无效状态标记)即可。可以不装满。 只需要全部初始化为 0,即可,一维 vs 二维二
2021-02-18 18:14:22 225
原创 【动态规划 + 二分查找】LeetCode - 300. 最长递增子序列
题目描述题目链接解法1:动态规划dp[i] 的值代表 nums 前 i 个数字的最长子序列长度转移方程: dp[i] = max(dp[i], dp[j] + 1) for j in [0, i)初始状态:dp[i] 所有元素置 1,含义是每个元素都至少可以单独成为子序列,此时长度都为 1。返回值:返回 dp 列表最大值,即可得到全局最长上升子序列长度。时间复杂度 O(n^2) : 遍历计算 dp 列表需 O(n),同时计算每个 dp[i] 需 O(n),双层遍历。空O(N)O(N) :
2021-02-17 21:17:29 340
原创 【二分查找】LeetCode - 153. 寻找旋转排序数组中的最小值
题目描述题目链接解法1:mid 和 right 比较二分法寻找最值 和 查找指定值 的思路一样。我们还是:初始化首尾指针 l 和 r,如果 nums[mid] 大于 nums[r],说明最小的一定在右侧,比如 [4, 5, 6, 1, 2],因此 l = mid + 1否则 r = mid(不可以 r = mid - 1,这里多判断等号没有意义,因为题目没有让我们找指定值)当 l >= r 或者 nums[l] < nums[r] 的时候退出循环。这是因为若 nums[l] &l
2021-02-17 20:04:38 79
原创 【二分查找】面试题 10.03. 搜索旋转数组
题目描述题目链接解法这是局部有序的二分查找,类似的题还有 33. 搜索旋转排序数组 和 81. 搜索旋转排序数组 II,再加上这一题,他们是难度是依次递增的,所以我直接拿这一题记录一下吧首先要知道,我们随便选择一个点,将数组分为前后两部分,其中一部分一定是有序的。我们可以先找出 mid,然后根据 mid 来判断,mid 是在有序的部分还是无序的部分假如 mid 小于 start,则 mid 一定在右边有序部分,即 [mid,end] 部分有序。假如 mid 大于 start,则 mid 一定
2021-02-17 18:48:41 144
原创 【快慢指针】LeetCode - 202. 快乐数
题目描述题目链接解法:快慢指针不建议用集合记录每次的计算结果来判断是否进入循环,因为这个集合可能大到无法存储;另外,也不建议使用递归,同理,如果递归层次较深,会直接导致调用栈崩溃每个数字都会根据 各位平方和 指向另一个数字,所以从任意数字开始进行 各位平方和 的迭代操作,就相当于在链表上游走。如果 无限循环 但始终变不到 1,那说明肯定是链表游走到了环。所以问题就转化成了快慢指针链表判环问题class Solution {public: int getNext(int n)
2021-02-16 16:01:47 92
原创 【并查集】LeetCode - 1361. 验证二叉树
题目描述题目链接解法1:并查集容易想到, 对于一棵树关键在于图中结点不能成环, 而对于连通性的判断, 很容易想到使用并查集对于并查集, 先判断当前结点与其左右子结点的连通性, 若之前已经连通, 那么本次操作就会成环; 若不连通则进行连通操作当所有结点操作完后, 在判断当前并查集的连通域数量,若为1, 则证明所有结点连通,即是一颗树二叉是肯定二叉的,因为题目输入就直接是左右子树class Solution { //并查集 class UnionFind{
2021-02-16 15:30:45 129
原创 【二分查找】LeetCode - 222. 完全二叉树的节点个数
题目描述题目链接解法1:深度优先遍历class Solution {public: int countNodes(TreeNode* root) { //深度优先遍历 if(!root) return 0; return countNodes(root->left) + countNodes(root->right) + 1; }};时间复杂度O(n),空间复杂度O(logn)(考虑递归栈)解法2:利用完全二
2021-02-15 23:41:23 115
原创 【二分查找】LeetCode - 875. 爱吃香蕉的珂珂
题目描述题目链接解法如果珂珂能以 K 的进食速度最终吃完所有的香蕉(在 H 小时内),那么她也可以用更快的速度吃完。当珂珂能以 K 的进食速度吃完香蕉时,我们令 possible(K) 为 true。举个例子,当初始条件为 piles = [3, 6, 7, 11] 和 H = 8 时,存在 X = 4 使得 possible(1) = possible(2) = possible(3) = False,且 possible(4) = possible(5) = … = True。我们可以二分
2021-02-15 22:21:06 163 1
原创 【二分查找】LeetCode - 33. 搜索旋转排序数组
题目描述题目链接解法在408真题上见过差不多的题,其实不是很难,但我废了好久,一直在死磕自己的那套想法。为了省时间,我就直接把思路截图了截图来自于https://leetcode-cn.com/problems/search-in-rotated-sorted-array/代码有两种class Solution {public: int search(vector<int>& nums, int target) { int n = (int)nu
2021-02-15 02:05:48 302 1
原创 【左右端点指针】LeetCode - 713. 乘积小于K的子数组
题目描述题目链接解法分析对于每个 right(i),我们需要找到最小的 left(j),满足 从 left 到 right 的数值乘积小于 k 。由于当 left 增加时,这个乘积是单调不增的,因此我们可以使用双指针的方法,单调地移动 left算法我们使用一重循环枚举 right,同时设置 left 的初始值为 0。在循环的每一步中,表示 right 向右移动了一位,将乘积乘以 nums[right]。当乘积大于等于 k 时我们需要向右移动 left,直到满足乘积小于 k 的条件。在每次移动时
2021-02-15 00:24:35 94
原创 【左右端点指针】LeetCode - 16. 最接近的三数之和
题目描述题目链接解法思路已经在【左右端点指针】LeetCode - 15. 三数之和 中较为详细地解释过了,不同的是,本题已经假定了只有唯一答案,所以不需要去重;这次不是和 0 比较,而是比较 abs(target-sum) 和 abs(target-ans)另外的两个小优化(其实15中也体现了)当我们枚举到恰好等于 target 的 a+b+c 时,可以直接返回 target 作为答案,因为不会有再比这个更接近的值了。当我们枚举 a,b,c 中任意元素并移动指针时,可以直接将其移动到下一个
2021-02-14 23:04:04 56
原创 【左右端点指针】LeetCode - 15. 三数之和
题目描述题目链接解法首先我发现左右端点指针的前提一般是有序序列,所以一般跟排序一块,这类题是这样,二分查找也是这样思路: 一个固定指针 k 遍历数组,两个动指针 i,j 分设在数组索引 (k,len(nums)) 两端,通过双指针交替向中间移动,记录对于每个固定指针 k 的所有满足题目要求 nums[k] + nums[i] + nums[j] == 0 的 i,j 组合当 k > 0且nums[k] == nums[k - 1]时即跳过此元素。因为已经将 nums[k - 1] 的所有
2021-02-14 22:55:26 88
原创 【前缀和】LeetCode - 1371. 每个元音包含偶数次的最长子字符串
题目描述题目链接解法暴力法 + 剪枝没有思路的时候就试试暴力法。也就是双层循环找到所有子串,然后对于每一个子串,统计元音个数,如果子串的元音个数都是偶数,则更新答案,最后返回最大的满足条件的子串长度即可。这里我用了一个小的 trick。枚举所有子串的时候,我是从最长的子串开始枚举的,这样我找到一个满足条件的直接返回就行了(early return),不必维护最大值。这样不仅减少了代码量,还提高了效率。也就是所说的 “剪枝”双层循环找出所有子串的复杂度是O(n^2),统计元音个数复杂度是O(
2021-02-14 20:34:57 186
原创 【前缀和】LeetCode - 1310. 子数组异或查询
题目描述题目链接解法前缀和即一个数组中,第 n 位存储的是数组前 n 个数字的和。对 [1,2,3,4,5,6] 来说,其前缀和可以是 pre=[1,3,6,10,15,21]。我们可以使用公式 pre[????]=pre[????−1]+nums[????] 得到每一位前缀和的值,从而通过前缀和进行相应的计算和解题。前缀和一般用在求 [i,j] 这样的二元问题上,可以转化为求 [0, j] - [0, i-1] ,就变成了 [0, x] 的一元问题前缀和在本题的应用首先很容易想到
2021-02-14 19:55:30 116
原创 【快慢指针】LeetCode - 80. 删除排序数组中的重复项 II
题目描述题目链接解法方法一:删除多余的重复项由于输入数组已经排序,所以重复项都显示在旁边。题目要求在原地修改数组,最简单的方法就是删除多余的重复项。对于数组中的每个数字,若出现 2 个以上的重复项,就将多余的重复项从数组列表中删除。这里的删除是指将待删除项后面的全部数据向前挪一格,显然,这种算法的复杂度是O(N^2)方法二:覆盖多余的重复项这需要两个指针,即双指针,或者说是快慢指针也无所谓,因为快慢指针的含义是两个移动速度或者别的方面没有联系的指针。到此,快慢指针的两个常见用法我都已经接
2021-02-14 00:38:32 76
原创 【快慢指针】LeetCode - 142. 环形链表 II
题目描述题目链接解法利用快慢指针 fastt 和 slow,fast 一次走两步,slow 一次走一步两指针相遇说明有环,第一次相遇时所走步数的差值正好是环的倍数,但是是多少倍不可知让 fast从头再走,slow 留在原地,fast 和 slow 均一次走一步,当两个指针第二次相遇时,均处在环的起点处,返回slow/** * Definition for singly-linked list. * struct ListNode { * int val; * L
2021-02-13 23:37:42 51
原创 【二分查找】LeetCode - 287. 寻找重复数
题目描述题目链接解法1:二分查找这题常规思路是哈希,另辟个数组也好,原地哈希也好,原地置换也好,在我的【原地哈希】专栏里有很多这类题型。但本题进阶要求是数组不能变,并且空间复杂度要O(1)二分查找的常见用法是确定一个有范围的整数。而题目给了给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n)即确定了范围,故而可以用二分二分思路:二分法的思路是先猜一个数(有效范围 [left, right]里的中间数 mid),然后统计原始数组中小于等于这个中
2021-02-13 22:50:10 293
原创 【原地哈希】LeetCode - 41.缺失的第一个正数
题目描述题目链接解法首先想到的是哈希,本题哈希的思路如下:假设原始数组为 A。先构造一个临时数组 tmp,初始化为 0,大小为A.size(). 遍历 A,把 A[i] 复制到 tmp[A[i]-1] 的位置。如果 A[i] - 1 超过了 tmp 的范围,就直接扔掉。如此一来,tmp[0…size) 中就保存了一部分 A 的值。然后从位置 0 开始检查 tmp,如果发现该位置的值和索引号不匹配,就说明找到了缺失的数了但是题目要求严格的空间复杂度了,那就原地哈希,准确的说是原地置换的思路和算法,
2021-02-13 22:20:22 96
原创 【原地哈希】剑指Offer 03.数组中重复的数字
题目描述解法1:原地哈希思路: 如果没有重复数字,那么正常排序后,数字i应该在下标为i的位置,所以思路是重头扫描数组,遇到下标为i的数字如果不是i的话,(假设为m), 那么我们就拿与下标m的数字交换。在交换过程中,如果有重复的数字发生,那么终止返回tureclass Solution {public: int findRepeatNumber(vector<int>& nums) { int len=nums.size(),tmp; fo
2021-02-13 21:58:38 96
原创 【原地哈希】LeetCode - 442.数组中重复的数据
题目描述解法由条件1 ≤ a[i] ≤ n,可知出nums 中的所有值可以和其索引有对应关系对应关系为 nums[i]的正负值可表示 值为i + 1是否出现,若出现则将其变为加上负号,即 nums[i] *= -1,默认为正整数即未出现比如输入:4 3 2 2 8 7 3 1依次遍历时遇见的4 3 2,把nums[3],[2],[1]都取负值变成了4 -3 -2 -2 8 2 3 1当遇到第二个2时,访问nums[1]发现值为负,说明2是重复出现的数class Solution {pub
2021-02-13 21:50:44 105
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人