leetcode 打气球
右端点排序,合并区间
美团种树
随着种树数量的增加,树总量单调递增,可以通过二分法查询种树量
美团流星
左端点和右端点分开排序,计算最大经过区间数量
右端点排序,合并区间
随着种树数量的增加,树总量单调递增,可以通过二分法查询种树量
左端点和右端点分开排序,计算最大经过区间数量
基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 'A'、'C'、'G' 和 'T' 之一。 假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。 例如,"AACCGGTT" --> "AACCGGTA" 就是一次基因变化。 另有一个基因库 bank 记录了所有有效的基因变化,只有基因库中的基因才是有效的基因序列。(变化后的基因必须位于基因库 bank 中) 给你两个基因序列 start 和 end ,以及一个基因库 bank ,请你找出并返回能够使 start 变化为 end 所需的最少变化次数。如果无法完成此基因变化,返回 -1 。 注意:起始基因序列 start 默认是有效的,但是它并不一定会出现在基因库中。
BFS
1 | int minMutation(string startGene, string endGene, vector<string>& bank) { |
小明的电动车电量充满时可行驶距离为 cnt,每行驶 1 单位距离消耗 1 单位电量,且花费 1 单位时间。小明想选择电动车作为代步工具。地图上共有 N 个景点,景点编号为 0 ~ N-1。他将地图信息以 [城市 A 编号,城市 B 编号,两城市间距离] 格式整理在在二维数组 paths,表示城市 A、B 间存在双向通路。初始状态,电动车电量为 0。每个城市都设有充电桩,charge[i] 表示第 i 个城市每充 1 单位电量需要花费的单位时间。请返回小明最少需要花费多少单位时间从起点城市 start 抵达终点城市 end。
通过 Dijkstra 方法,更新起点到各个顶点的最短花费单位时间 代码参考
1 | int electricCarPlan(vector<vector<int>>& paths, int cnt, int start, int end, vector<int>& charge) { |
给你一个大小为 n x n 的整数矩阵 board ,方格按从 1 到 n2 编号,编号遵循 转行交替方式 ,从左下角开始 (即,从 board[n - 1][0] 开始)每一行交替方向。
玩家从棋盘上的方格 1 (总是在最后一行、第一列)开始出发。
每一回合,玩家需要从当前方格 curr 开始出发,按下述要求前进:
选定目标方格 next ,目标方格的编号符合范围 [curr + 1, min(curr + 6, n2)] 。 该选择模拟了掷 六面体骰子 的情景,无论棋盘大小如何,玩家最多只能有 6 个目的地。 传送玩家:如果目标方格 next 处存在蛇或梯子,那么玩家会传送到蛇或梯子的目的地。否则,玩家传送到目标方格 next 。 当玩家到达编号 n2 的方格时,游戏结束。 r 行 c 列的棋盘,按前述方法编号,棋盘格中可能存在 “蛇” 或 “梯子”;如果 board[r][c] != -1,那个蛇或梯子的目的地将会是 board[r][c]。编号为 1 和 n2 的方格上没有蛇或梯子。
注意,玩家在每回合的前进过程中最多只能爬过蛇或梯子一次:就算目的地是另一条蛇或梯子的起点,玩家也 不能 继续移动。
举个例子,假设棋盘是 [[-1,4],[-1,3]] ,第一次移动,玩家的目标方格是 2 。那么这个玩家将会顺着梯子到达方格 3 ,但 不能 顺着方格 3 上的梯子前往方格 4 。 返回达到编号为 n2 的方格所需的最少移动次数,如果不可能,则返回 -1。
将棋盘的各个格子看作图顶点,顶点之间的移动步数作为边长,可将问题转换为最短路径问题。 在使用 Dijkstra 算法时可能会遇到一些细节问题:
在建立连接路径时
不能连续跳跃,且必须跳跃
可以不使用优先队列
给你一个数组 routes ,表示一系列公交线路,其中每个 routes[i] 表示一条公交线路,第 i 辆公交车将会在上面循环行驶。
例如,路线 routes[0] = [1, 5, 7] 表示第 0 辆公交车会一直按序列 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... 这样的车站路线行驶。 现在从 source 车站出发(初始时不在公交车上),要前往 target 车站。 期间仅可乘坐公交车。
求出 最少乘坐的公交车数量 。如果不可能到达终点车站,返回 -1 。
因为只需要统计路线数,所以不需要考虑经过节点数量 因此,可以将路线作为节点,有重合站点的路线建立边,再统计起始线路到达目标线路的经过路线数量
1 | int numBusesToDestination(vector<vector<int>>& routes, int source, int target) { |
给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] = [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi = values[i] 。每个 Ai 或 Bi 是一个表示单个变量的字符串。 另有一些以数组 queries 表示的问题,其中 queries[j] = [Cj, Dj] 表示第 j 个问题,请你根据已知条件找出 Cj / Dj = ? 的结果作为答案。 返回 所有问题的答案 。如果存在某个无法确定的答案,则用 -1.0 替代这个答案。如果问题中出现了给定的已知条件中没有出现的字符串,也需要用 -1.0 替代这个答案。 注意:输入总是有效的。你可以假设除法运算中不会出现除数为 0 的情况,且不存在任何矛盾的结果。 注意:未在等式列表中出现的变量是未定义的,因此无法确定它们的答案。
1 | vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) { |
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。 请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
1 | bool canFinish(int numCourses, vector<vector<int>>& prerequisites) { |
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
常规做法是通过一维数组存储当前位置作为结尾的最大子序列长度。 而根据推理可知,若在从左向右遍历时,当前元素不能与已有元素构成更长的子序列,那么替换更小的元素将有更大的可能性构成更长子序列。
1 | int lengthOfLIS(vector<int>& nums) { |
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
给你一个满足下述两条属性的 m x n 整数矩阵: 每行中的整数从左到右按非严格递增顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。
1 |
|
每次迭代有 N 个选择,将 N 个选择放入堆得到排序后的值
给你一个整数 n ,请你找出并返回第 n 个 丑数 。 丑数 就是质因子只包含 2、3 和 5 的正整数。
1 | int nthUglyNumber(int n) { |
给定两个以 非递减顺序排列 的整数数组
1 | vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) { |
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
1 | int lengthOfLastWord(string s) { |
在匹配之前需要计算匹配字符串各个位置的最长重合前后缀。初始化第一个位置为 0,后缀末尾位置从 1 开始,若当前前缀的末尾与后缀末尾相同,则更新长度;否则根据当前前缀寻找新的重合前缀,这个阶段是最难理解的部分,如下图所示。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。
1 | int strStr(string haystack, string needle) { |
返回 s 的任意可能的重新排列。若不可行,返回空字符串 "" 。
给定一个 无重复元素 的 有序 整数数组 nums 。 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x 。 列表中的每个区间范围 [a,b] 应该按如下格式输出: "a->b" ,如果 a != b "a" ,如果 a == b
1 | class Solution { |
单词数组 words 的 有效编码 由任意助记字符串 s 和下标数组 indices 组成,且满足: words.length == indices.length 助记字符串 s 以 '#' 字符结尾 对于每个下标 indices[i] ,s 的一个从 indices[i] 开始、到下一个 '#' 字符结束(但不包括 '#')的 子字符串 恰好与 words[i] 相等 给你一个单词数组 words ,返回成功对 words 进行编码的最小助记字符串 s 的长度 。
若字符串s1完全与字符串s2的末尾部分或整体重合,则字符串s1可作为同一部分助记字符串 因此,可以使用Trie构建字符串树 为了正确计算包含关系,需要优先按照字符串长度倒序排序字符串
1 | class Solution { |
给你一个链表数组,每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中,返回合并后的链表。
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其总和大于等于 target 的长度最小的子数组[
确定左值,向右扩展直到满足 target,记录当前窗口大小,移动左值,重复上述动作,直到右值不能再拓展
1 | class Solution { |
根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。 给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律: 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡; 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活; 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡; 如果死细胞周围正好有三个活细胞,则该位置死细胞复活; 下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。
使用二进制的第二位存储修改后的状态。
1 | class Solution { |
请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
分别从行、列和九宫格统计数字出现次数
1 | bool isValidSudoku(vector<vector<char>>& board) { |
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性: 每行的元素从左到右升序排列。 每列的元素从上到下升序排列。
1 | bool searchMatrix(vector<vector<int>>& matrix, int target) { |
给定一个包含若干个点的集合,求其中任意两点之间的最大/最小曼哈顿距离。
由上述式子可得:
整理合并可得:
所以,问题转换为比较点
1 | vector<int> d1; |
给定一个整数 n ,返回 n! 结果中尾随零的数量。 提示
1 | int trailingZeroes(int n) { |
给你两个整数 left 和 right ,表示区间 [left, right] ,返回此区间内所有数字 按位与 的结果(包含 left 、right 端点)。
相邻两个数相与,末尾的 0 将会被消除,通过移位找公共前缀得到区间相与结果
1 | int rangeBitwiseAnd(int left, int right) { |
单链表模拟
取余推测
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums 之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
正反向前缀积,使用中间变量记录累积
1 | class Solution { |
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。 由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。 注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,
将
其中,式子
1 | double myPow(double x, int n) { |
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。
统计每个二进制位 1 的出现次数,若只出现一次的数在该为为 0,那么统计次数应为 3 的倍数,否则有余数
1 | int singleNumber(vector<int>& nums) { |
另一种方法通过状态机的思想,用一个两位二进制数统计当前二进制位的状态,00 -> 01 -> 10 -> 00
1 | int singleNumber(vector<int>& nums) { |
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。
1 | int majorityElement(vector<int>& nums) { |
参考题解推荐了这种方法,思考方向为若与当前假设的众数相同,则投赞成票 1,否则投否认票-1,当得票为 0 时,则继续进行假设,多数元素将获得更多投票将投票值稳定为正;另外,从代码上理解,多数元素总有更多数量的连续元素
1 | int majorityElement(vector<int>& nums) { |
准备多种大小的背包,当物品体积小于背包时,可尝试比较当前已放置物品价值,与当前物品价值和更小体积背包放置物品价值之和,取更大值
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
1 | bool canPartition(vector<int>& nums) { |
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
1 | bool wordBreak(string s, vector<string>& wordDict) { |
X 星人发现了一个藏宝图,在藏宝图中标注了 N 个宝库的位置。这 N 个宝库连成了一条直线,每个宝库都有若干枚金币。 X 星人决定乘坐热气球去收集金币,热气球每次最多只能飞行 M 千米(假设热气球在飞行过程中并不会发生故障)此外,由于设计上的缺陷,热气球最多只能启动 K 次。 X 星人带着热气球来到了第 1 个宝库(达到第 1 个宝库时热气球尚未启动),收集完第 1 个宝库的金币后将启动热气球前往下一个宝库。如果他决定收集某一个宝库的金币,必须停下热气球,收集完之后再重新启动热气球。当然,X 星人每到一个宝库是一定会拿走这个宝库所有金币的。 已知每一个宝库距离第 1 个宝库的距离(单位:千米)和宝库的金币数量。 请问 X 星人最多可以收集到多少枚金币?
输入描述: 单组输入。
第 1 行输入三个正整数 N、M 和 K,分别表示宝库的数量、热气球每次最多能够飞行的距离(千米)和热气球最多可以启动的次数,三个正整数均不超过 100,相邻两个正整数之间用空格隔开。
接下来 N 行每行包含两个整数,分别表示第 1 个宝库到某一个宝库的距离(千米)和这个宝库的金币枚数。(因为初始位置为第 1 个宝库,因此第 1 个宝库所对应行的第 1 个值为 0。)
输入保证所有的宝库按照到第 1 个宝库的距离从近到远排列,初始位置为第 1 个宝库。
输出描述: 输出一个正整数,表示最多可以收集的金币数。
示例 1 输入例子: 5 10 2 0 5 8 6 10 8 18 12 22 15 输出例子: 25 例子说明: X 星人启动热气球两次,分别收集第 1 个、第 3 个和第 4 个宝库的金币,一共可以得到的金币总数为 5+8+12=25 枚。
1 |
|
在一个由 '0' 和 '1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。
1 | dp[i][j] = min(min(dp[i - 1][j - 1], dp[i - 1][j]), dp[i][j - 1]) |
状态定义
其中
1 | class Solution { |
给定一个长度为 n 的环形整数数组 nums ,返回 nums 的非空 子数组 的最大可能和 。 环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 nums[(i + 1) % n] , nums[i] 的前一个元素是 nums[(i - 1 + n) % n] 。 子数组 最多只能包含固定缓冲区 nums 中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], ..., nums[j] ,不存在 i <= k1, k2 <= j 其中 k1 % n == k2 % n 。
通常对于环形数组的最大子数组和有两种情形,一种是不需要首尾相连的子数组,另一种则是需要连接开头的子数组 对于子数组 1,其求解方式与传统求解方法一样; 对于子数组 2,可以通过逆向思维求解,由于子数组 2 为最大子数组,所以数组的和减去子数组 2 的和,得到的是最小子数组 3,子数组 3 的首位是不相连的
1 | int maxSubarraySumCircular(vector<int>& nums) { |
恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。 骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。 有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。 为了尽快解救公主,骑士决定每次只 向右 或 向下 移动一步。 返回确保骑士能够拯救到公主所需的最低初始健康点数。 注意:任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。
对于任意位置,其起始健康点数取决于后续经过的房间,所以尝试使用逆向 DP
解题
对比理解,若问题为到达终点的最大健康点数,对于任意位置,其最大健康点数只与已经经过的房间有关,可以使用正向
DP 解题
根据观察发现,当后续房间值为非负值时,对起始健康点数无影响;而当后续房间值为负值时,起始健康点数应为负值的相反数。由此可得到状态转移方程为:
时间复杂度:
1 | int calculateMinimumHP(vector<vector<int>>& dungeon) { |
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
可考虑两种可能的情况,一种为小偷偷了第一家,最后一家就不能偷;第二种情况为小偷没有偷第一家