Leetcode review
399. Evaluate Division
问题描述:
You are given an array of variable pairs equations
and an array of real numbers values
, where equations[i] = [Ai, Bi]
and values[i]
represent the equation Ai / Bi = values[i]
. Each Ai
or Bi
is a string that represents a single variable.
You are also given some queries
, where queries[j] = [Cj, Dj]
represents the jth
query where you must find the answer for Cj / Dj = ?
.
Return the answers to all queries. If a single answer cannot be determined, return -1.0
.
Note: The input is always valid. You may assume that evaluating the queries will not result in division by zero and that there is no contradiction.
Note: The variables that do not occur in the list of equations are undefined, so the answer cannot be determined for them.
Example 1:
1 | Input: equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]] |
Example 2:
1 | Input: equations = [["a","b"],["b","c"],["bc","cd"]], values = [1.5,2.5,5.0], queries = [["a","c"],["c","b"],["bc","cd"],["cd","bc"]] |
Example 3:
1 | Input: equations = [["a","b"]], values = [0.5], queries = [["a","b"],["b","a"],["a","c"],["x","y"]] |
这是一道典型的图论相关问题,思路是构造一个邻接矩阵matrix
, matrix[i][j]
代表i/j
的值,如果没有这个值就为0。
构造好这个矩阵之后就可以通过BFS去递归的寻找关系,比如在Example 1中需要找到a/c
,那么就可以先通过dfs找到a/b
,然后再通过dfs进一步找到b/c
从而获得a/c
。
具体的代码思路如下
1 | class Solution { |
时间复杂度分析:
构建map映射,即字符串和matrix中对应下标时的复杂度为
O(E)
,E为Equations的大小。构建邻接矩阵时的复杂度为
O(E)
。dfs方法在调用时最坏情况下会遍历邻接矩阵的每一个值,假设共有n个不同的字符串,即邻接矩阵有n^2个值,所以时间复杂度为
O(n^2)
。for循环遍历queries时,假设共有Q个query,那么每次调用一次dfs,时间复杂度为
O(Q * n^2)
综上时间复杂度为O(E + Q * n^2)
。
空间复杂度分析:
- matrix共有n^2个节点,复杂度为
O(n^2)
- map共存储n个节点,复杂度为
O(n)
- dfs时递归的深度可能会达到所有节点,复杂度为
O(n)
- dfs时的set最坏情况会包含所有节点,复杂度为
O(n)
- res数组假设有Q个query,复杂度为
O(Q)
综上空间复杂度为O(Q + n^2)
994. Rotting Oranges
问题描述:
![](https://pics.findfuns.org/Problem 994.png)
思路是先遍历grid,如果找到“烂橘子”就加入到Queue中,遍历完成之后对Queue中的所有“烂橘子”进行BFS并记录每次BFS的时间,动态更新最长时间。最后检查一遍grid,如果还存在新鲜的橘子就返回-1,如果没有就返回最长时间。
代码如下:
1 | class Solution { |
时间复杂度分析:
- 两个for循环的时间复杂度为
O(n*m)
,n和m分别为grid的长和宽 - bfs最坏情况下会遍历每一个节点,时间复杂度为O(n*m)。
综上时间复杂度为O(n*m)
空间复杂度分析:
- queue最坏情况下会包含所有节点,空间复杂度为
O(n*m)
综上空间复杂度为O(n*m)
875. Koko Eating Bananas
Koko loves to eat bananas. There are n
piles of bananas, the ith
pile has piles[i]
bananas. The guards have gone and will come back in h
hours.
Koko can decide her bananas-per-hour eating speed of k
. Each hour, she chooses some pile of bananas and eats k
bananas from that pile. If the pile has less than k
bananas, she eats all of them instead and will not eat any more bananas during this hour.
Koko likes to eat slowly but still wants to finish eating all the bananas before the guards return.
Return the minimum integer k
such that she can eat all the bananas within h
hours.
Example 1:
1 | Input: piles = [3,6,7,11], h = 8 |
Example 2:
1 | Input: piles = [30,11,23,4,20], h = 5 |
Example 3:
1 | Input: piles = [30,11,23,4,20], h = 6 |
可以使用二分查找不断缩小符合要求的最小速度,直到找到最小的速度
代码如下:
1 | class Solution { |
因为每一堆香蕉的最大数量为10^9,所以可以设置最大速度为10^9,最小速度为1。然后就可以进行二分查找,不断缩小速度。
值得注意的是在check()方法
中用于记录用时的变量hours
需要设置为long,不然会有溢出的风险。
时间复杂度分析:
- piles的长度为n,每次二分查找都需要遍历一次piles数组,复杂度为
O(n)
- 二分查找的范围是1-10^9,所以复杂度为
O(10^9)
- 综上时间复杂度为
O(10^9n)
空间复杂度为O(1)
962. Maximum Width Ramp
A ramp in an integer array nums
is a pair (i, j)
for which i < j
and nums[i] <= nums[j]
. The width of such a ramp is j - i
.
Given an integer array nums
, return the maximum width of a ramp in nums
. If there is no ramp in nums
, return 0
.
Example 1:
1 | Input: nums = [6,0,8,2,1,5] |
Example 2:
1 | Input: nums = [9,8,1,0,1,9,4,0,4,1] |
这道题其实简单来说就是找到和每个元素对应的不小于它的最远的元素,并且得到距离的最大值。
具体的做法是维护一个单调递减栈,从左向右遍历。再从右向左遍历,如果栈顶的元素小于等于遍历到的元素,就出栈并记录最大距离,知道栈空。
代码如下:
1 | class Solution { |
时间复杂度分析:
- 维护单调栈需要遍历每个元素,
O(n)
。 - 第二次从右向左遍历元素最坏情况下需要遍历全部元素,
O(n)
。
时间复杂度为O(n)
。
空间复杂度分析:
- 最坏情况
stack
需要记录全部元素,O(n)
。
空间复杂度为O(n)
。
2406. Divide Intervals Into Minimum Number of Groups
You are given a 2D integer array intervals
where intervals[i] = [lefti, righti]
represents the inclusive interval [lefti, righti]
.
You have to divide the intervals into one or more groups such that each interval is in exactly one group, and no two intervals that are in the same group intersect each other.
Return the minimum number of groups you need to make.
Two intervals intersect if there is at least one common number between them. For example, the intervals [1, 5]
and [5, 8]
intersect.
Example 1:
1 | Input: intervals = [[5,10],[6,8],[1,5],[2,3],[1,10]] |
Example 2:
1 | Input: intervals = [[1,3],[5,6],[8,10],[11,13]] |
这道题可以用时间线和事件的思路去解决,不必拘泥于时间对,而是把每一个时间对拆开,将其视为开始时间和结束时间。
同时使用一个最小堆对时间进行排序,当存在开始时间和结束时间相同时先处理开始时间。维护一个变量记录同时存在的事件的最大数量,即为答案。
代码如下:
1 | class Solution { |
时间复杂度分析:
- 假设intervals有n个事件,那么一共有2n个时间点,插入堆的复杂度为
O(log2n)
,综合复杂度为O(2nlog(2n)) = O(nlogn)
。
空间复杂度分析:
- 堆需要
O(2n) = O(n)
的空间
632. Smallest Range Covering Elements from K Lists
You have k
lists of sorted integers in non-decreasing order. Find the smallest range that includes at least one number from each of the k
lists.
We define the range [a, b]
is smaller than range [c, d]
if b - a < d - c
or a < c
if b - a == d - c
.
Example 1:
1 | Input: nums = [[4,10,15,24,26],[0,9,12,20],[5,18,22,30]] |
Example 2:
1 | Input: nums = [[1,2,3],[1,2,3],[1,2,3]] |
涉及到“最大”“最小”问题时,往往需要考虑使用heap,因为这类问题需要频繁地获得最大最小值,而堆可以实现在O(1)
的复杂度下得到最值,从而降低时间复杂度。
每一轮将一列元素存入heap,将最小值移出,并且动态的更新最大最小值,并记录range,再将移出元素的下一个元素加入heap,直到heap中的元素数量小于nums中数组的数量,最终获得的最小range即为答案。
1 | class Solution { |
1 | 例一的每一轮处理过程如下 |
时间复杂度分析:
- 设nums中共有k个数组,设共有N个元素,最坏情况下heap需要遍历每一个元素,每次插入和删除元素的复杂度为
O(logk)
,时间复杂度为O(Nlogk)
。
空间复杂度分析:
- heap中始终存在k个元素,空间复杂度为
O(k)
1942. The Number of the Smallest Unoccupied Chair
There is a party where n
friends numbered from 0
to n - 1
are attending. There is an infinite number of chairs in this party that are numbered from 0
to infinity
. When a friend arrives at the party, they sit on the unoccupied chair with the smallest number.
- For example, if chairs
0
,1
, and5
are occupied when a friend comes, they will sit on chair number2
.
When a friend leaves the party, their chair becomes unoccupied at the moment they leave. If another friend arrives at that same moment, they can sit in that chair.
You are given a 0-indexed 2D integer array times
where times[i] = [arrivali, leavingi]
, indicating the arrival and leaving times of the ith
friend respectively, and an integer targetFriend
. All arrival times are distinct.
Return the chair number that the friend numbered targetFriend
will sit on.
Example 1:
1 | Input: times = [[1,4],[2,3],[4,6]], targetFriend = 1 |
Example 2:
1 | Input: times = [[3,10],[1,5],[2,6]], targetFriend = 0 |
- 这个问题可以考虑将每个人的时间拆开,分成开始时间和结束时间。将拆开后的时间以数组的形式存入小顶堆中,arr[0]是时间,arr[1]用来记录是第几个人,arr[2]用来记录是到达时间还是离开时间。以arr[0]为依据排序。
- 同时,一个优先队列来记录当前时刻下空的椅子,这样总是可以得到最小的可以利用的椅子。
- 用一个Map来记录当前时刻下已经被占用的椅子,key为人的序号,value是椅子序号。
- 具体的流程是,按发生顺序依次遍历每一个时间。如果为到达时间,就从availableChairs中分配一把椅子,并存入occupiedChairs。并且检查是否是targetFriend;如果为结束时间,就将对应的occupiedChairs中的椅子放回avaliableChairs。
1 | class Solution { |
时间复杂度分析:
- 设一共有n个人,events共插入2n次,每次插入的时间复杂度为
O(logn)
,复杂度为O(2nlog2n) = O(nlogn)
。 - 将所有椅子加入avaliableChairs,时间复杂度为
O(nlogn)
。 - 依次遍历每个event,最坏情况下每次都要取一个新的椅子,从avaliableChairs取椅子复杂度为
O(logn)
, 插入occupiedChairs复杂度为(O(1)),总复杂度为2n(O(logn) + O(1)) = O(nlogn)
。
综上时间复杂度为O(nlogn)
。
空间复杂度分析:
- events需要
O(2n)
- avaliableChair最多需要
O(n)
- occupiedChair最多需要
O(n)
综上空间复杂度为O(n)
。
632. Smallest Range Covering Elements from K Lists
You have k
lists of sorted integers in non-decreasing order. Find the smallest range that includes at least one number from each of the k
lists.
We define the range [a, b]
is smaller than range [c, d]
if b - a < d - c
or a < c
if b - a == d - c
.
Example 1:
1 | Input: nums = [[4,10,15,24,26],[0,9,12,20],[5,18,22,30]] |
Example 2:
1 | Input: nums = [[1,2,3],[1,2,3],[1,2,3]] |
Constraints:
nums.length == k
1 <= k <= 3500
1 <= nums[i].length <= 50
-10^5 <= nums[i][j] <= 10^5
nums[i]
is sorted in non-decreasing order.
这个题的核心在于要保证选取的范围至少包含每个子数组中一个元素。考虑到这一点可以在遍历时使用一个容器,并始终保证容器中恰好有k个元素(k为子数组的数量)。同时在每次遍历时需要获得最大值和最小值来确定范围,所以理所应当使用一个最小堆。
- 初始化:先把每个子数组的第一个元素放入堆中并记录最大值。
- 遍历:一个while循环,每次取出一个元素(最小值),更新最小值,从而更新最小范围。如果最小范围变小就记录当前的状态。
- 在遍历的最后把取出的最小元素的下一个元素(如果有)加入堆中,并更新最大值。
- 循环结束时记录的状态即为答案。
代码如下:
1 | class Solution { |
时间复杂度分析:
- 设共有N个元素, 最坏情况下需要遍历所有元素。
- 每个元素只进行一次进出堆的操作,堆中元素的数量始终为K,进入堆时维护最小堆的复杂度为
O(logK)
,出堆时的复杂度为O(1)
, 维护堆的时间复杂度为O(logK)
。共有N个元素,重复N次,时间复杂度为O(N*(O(logK) + O(logK) + O(1))) = O(NlogK)
。
空间复杂度分析:
- 堆中最多有K个元素,K为nums的size。空间复杂度为
O(K)
。
2406. Divide Intervals Into Minimum Number of Groups
You are given a 2D integer array intervals
where intervals[i] = [lefti, righti]
represents the inclusive interval [lefti, righti]
.
You have to divide the intervals into one or more groups such that each interval is in exactly one group, and no two intervals that are in the same group intersect each other.
Return the *minimum* number of groups you need to make.
Two intervals intersect if there is at least one common number between them. For example, the intervals [1, 5]
and [5, 8]
intersect.
Example 1:
1 | Input: intervals = [[5,10],[6,8],[1,5],[2,3],[1,10]] |
Example 2:
1 | Input: intervals = [[1,3],[5,6],[8,10],[11,13]] |
Constraints:
1 <= intervals.length <= 10^5
intervals[i].length == 2
1 <= lefti <= righti <= 10^6
可以画一个时间轴,把intervals中的每个元素当成一个时间段画在时间轴上,重叠时间段最多的时间点对应的重叠的数量就是最小需要的分组数。
- 把每个时间段拆成开始时间和结束时间,放入最小堆。
- 依次遍历堆中每一个元素,在遍历时,记录同时存在的时间段的最大数量即为答案。
代码如下:
1 | class Solution { |
时间复杂度分析:
- 设intervals数组共有N个元素,则共有2N个元素需要进栈和出栈各一次。时间复杂度为
O(2N*2*O(log2N)) = O(NlogN)
。
空间复杂度分析:
- 堆中最多存在2N个元素,空间复杂度为
O(2N) = O(N)
。
962. Maximum Width Ramp
A ramp in an integer array nums
is a pair (i, j)
for which i < j
and nums[i] <= nums[j]
. The width of such a ramp is j - i
.
Given an integer array nums
, return the maximum width of a *ramp* in nums
. If there is no ramp in nums
, return 0
.
Example 1:
1 | Input: nums = [6,0,8,2,1,5] |
Example 2:
1 | Input: nums = [9,8,1,0,1,9,4,0,4,1] |
Constraints:
2 <= nums.length <= 5 * 10^4
0 <= nums[i] <= 5 * 10^4
第一种方法是把所有元素按从小到大排序,再遍历一遍,遍历过程中维护最小下标并更新最长ramp的距离。
1 | class Solution { |
时间复杂度分析:
- 共有N个元素,排序时间复杂度为
O(NlogN)
。 - 遍历一遍复杂度为
O(N)
。 - 综合时间复杂度为
O(N + NlogN) = O(NlogN)
。
第二种方法较为巧妙,采用一个单调栈(单调递减)记录nums中的元素,再倒着遍历,当栈顶元素不大于当前元素时出栈并更新最大值,直到栈顶元素大于当前元素或栈为空。
单调栈的作用在这里其实是一个按倒序排序的”set“, 效果是记录了每一个可能入栈的元素的最左边位置(如果有多个相同的元素,只记录最左边的那个),这样就满足了最大ramp的要求。
1 | class Solution { |
时间复杂度分析:
- 构建单调栈的过程需要遍历整个数组,复杂度为
O(N)
。 - 第二次遍历最坏情况下需要遍历整个数组,复杂度为
O(N)
。 - 综合复杂度为
O(N)
。
3152. Special Array II
An array is considered special if every pair of its adjacent elements contains two numbers with different parity.
You are given an array of integer nums
and a 2D integer matrix queries
, where for queries[i] = [fromi, toi]
your task is to check that subarray nums[fromi..toi]
is special or not.
Return an array of booleans answer
such that answer[i]
is true
if nums[fromi..toi]
is special.
Example 1:
Input: nums = [3,4,1,2,6], queries = [[0,4]]
Output: [false]
Explanation:
The subarray is [3,4,1,2,6]
. 2 and 6 are both even.
Example 2:
Input: nums = [4,3,1,6], queries = [[0,2],[2,3]]
Output: [false,true]
Explanation:
- The subarray is
[4,3,1]
. 3 and 1 are both odd. So the answer to this query isfalse
. - The subarray is
[1,6]
. There is only one pair:(1,6)
and it contains numbers with different parity. So the answer to this query istrue
.
Constraints:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^5
1 <= queries.length <= 10^5
queries[i].length == 2
0 <= queries[i][0] <= queries[i][1] <= nums.length - 1
第一种方法:
- 先遍历数组,如果找到都是偶数(或奇数)的pair,将较小的下标存入list
- 对每一个query
- 进行二分查找
- 尝试在list中找到query[0] ~ query[1]范围内的值
- 找到结果为false,没找到为true
1 | class Solution { |
时间复杂度分析:
- 设共有N个元素,遍历一遍复杂度为
O(N)
- 设共有Q个query,对每一个query进行二分查找,复杂度为
O(QlogN)
- 综合复杂度为
O(N + QlogN)
空间复杂度分析:
- 需要
O(Q)
的空间来存储同奇偶对
还有一种效率更高的解法:
遍历数组,初始化prefixSum,
prefixSum[0] = 0
如果当前元素和上一个元素同为奇偶,
prefixSum[i] = prefixSum[i - 1] + 1
对每一个query,如果
query[1] != query[0]
说明范围内有相邻的奇数或偶数
1 | class Solution { |
时间复杂度分析:
- 遍历一遍数组
O(N)
- 遍历Query
O(Q)
- 综合复杂度
O(N + Q)
空间复杂度分析:
- prefixSum需要额外的
O(N)
空间
2779. Maximum Beauty of an Array After Applying Operation
You are given a 0-indexed array nums
and a non-negative integer k
.
In one operation, you can do the following:
- Choose an index
i
that hasn’t been chosen before from the range[0, nums.length - 1]
. - Replace
nums[i]
with any integer from the range[nums[i] - k, nums[i] + k]
.
The beauty of the array is the length of the longest subsequence consisting of equal elements.
Return the *maximum* possible beauty of the array nums
after applying the operation any number of times.
Note that you can apply the operation to each index only once.
A subsequence of an array is a new array generated from the original array by deleting some elements (possibly none) without changing the order of the remaining elements.
Example 1:
1 | Input: nums = [4,6,1,2], k = 2 |
Example 2:
1 | Input: nums = [1,1,1,1], k = 10 |
- Constraints:
1 <= nums.length <= 10^5
0 <= nums[i], k <= 10^5
这个题类似于扎气球问题452. Minimum Number of Arrows to Burst Balloons, 本质上都是区间问题。但这个问题变成了如何用一支箭扎破最多的气球(区间)。
虽然是子序列问题,但实际上最终的目的是找重叠数量最多的区间,所以排序是可以的。
思路是先对数组进行排序,再遍历每个区间的末尾端点, 用二分查找尝试找到第一个大于当前末尾端点的起始端点,记录此时的区间数量,每次遍历都进行更新,最终得到的就是最大值。
代码如下:
1 | class Solution { |
时间复杂度:
- 排序
O(NlogN)
- 外层循环
O(N)
,内层循环是一个二分查找O(logn)
,综合为O(NlogN)
- 综合复杂度
O(NlogN)
空间复杂度:O(1)
2981. Find Longest Special Substring That Occurs Thrice I
You are given a string s
that consists of lowercase English letters.
A string is called special if it is made up of only a single character. For example, the string "abc"
is not special, whereas the strings "ddd"
, "zz"
, and "f"
are special.
Return the length of the *longest special substring* of s
which occurs *at least thrice*, or -1
if no special substring occurs at least thrice.
A substring is a contiguous non-empty sequence of characters within a string.
Example 1:
1 | Input: s = "aaaa" |
Example 2:
1 | Input: s = "abcdef" |
Example 3:
1 | Input: s = "abcaba" |
Constraints:
3 <= s.length <= 50
s
consists of only lowercase English letters.
最简单直接的想法是对每个字母,从长度为1到长度为n遍历查找。但是时间复杂度过高。
实际上观察一下最长子串的规律就不难发现只需要记录每个字母的最长子串和次长子串的长度和数量:
- 最长子串数量 >= 3 — 最长子串长度
- 最长子串数量 == 2 — 最长子串长度 - 1
- 最长子串数量 == 1
- 次长子串长度 == 最长子串长度 - 1 — 最长子串长度 - 1
- 次长子串长度 < 最长子串长度 - 1 — 最长子串长度 - 2
1 | class Solution { |
时间复杂度:
- 遍历字符串
O(n)
- update方法复杂度为
O(1)
- 遍历map复杂度为
O(26 * 4) == O(1)
- 综合复杂度
O(N)
空间复杂度:
- Map需要额外的
O(26 * 4) == O(1)
3381. Maximum Subarray Sum With Length Divisible by K
You are given an array of integers nums
and an integer k
.
Return the maximum sum of a subarray of nums
, such that the size of the subarray is divisible by k
.
Example 1:
Input: nums = [1,2], k = 1
Output: 3
Explanation:
The subarray [1, 2]
with sum 3 has length equal to 2 which is divisible by 1.
Example 2:
Input: nums = [-1,-2,-3,-4,-5], k = 4
Output: -10
Explanation:
The maximum sum subarray is [-1, -2, -3, -4]
which has length equal to 4 which is divisible by 4.
Example 3:
Input: nums = [-5,1,2,-3,4], k = 2
Output: 4
Explanation:
The maximum sum subarray is [1, 2, -3, 4]
which has length equal to 4 which is divisible by 2.
Constraints:
1 <= k <= nums.length <= 2 * 10^5
-10^9 <= nums[i] <= 10^9
对于k=1的情况,可以使用kadane算法在线性的时间复杂度下得到最大子数组和。
如果K大于1,可以将[i, i + k], [i + k, i + 2 * k]….每个子数组当成一个元素,这样就可以使用kadane算法了。
代码如下:
1 | class Solution { |
时间复杂度:
- 计算前缀和
O(N)
- 外层循环复杂度为
O(K)
,内层循环复杂度为O(N/K)
- Kadane算法复杂度为
O(N/K)
- 综合复杂度为
O(K * (N / K + N / K)) = O(N)
空间复杂度:
prefix需要额外的
O(N)
对于临时tmp数组最大需要
O(N/K)
综合空间复杂度为
O(N)
3376. Minimum T3376. Minimum Time to Break Locks I
Bob is stuck in a dungeon and must break n
locks, each requiring some amount of energy to break. The required energy for each lock is stored in an array called strength
where strength[i]
indicates the energy needed to break the ith
lock.
To break a lock, Bob uses a sword with the following characteristics:
- The initial energy of the sword is 0.
- The initial factor
X
by which the energy of the sword increases is 1. - Every minute, the energy of the sword increases by the current factor
X
. - To break the
ith
lock, the energy of the sword must reach at leaststrength[i]
. - After breaking a lock, the energy of the sword resets to 0, and the factor
X
increases by a given valueK
.
Your task is to determine the minimum time in minutes required for Bob to break all n
locks and escape the dungeon.
Return the minimum time required for Bob to break all n
locks.
Example 1:
Input: strength = [3,4,1], K = 1
Output: 4
Explanation:
Time | Energy | X | Action | Updated X |
---|---|---|---|---|
0 | 0 | 1 | Nothing | 1 |
1 | 1 | 1 | Break 3rd Lock | 2 |
2 | 2 | 2 | Nothing | 2 |
3 | 4 | 2 | Break 2nd Lock | 3 |
4 | 3 | 3 | Break 1st Lock | 3 |
The locks cannot be broken in less than 4 minutes; thus, the answer is 4.
Example 2:
Input: strength = [2,5,4], K = 2
Output: 5
Explanation:
Time | Energy | X | Action | Updated X |
---|---|---|---|---|
0 | 0 | 1 | Nothing | 1 |
1 | 1 | 1 | Nothing | 1 |
2 | 2 | 1 | Break 1st Lock | 3 |
3 | 3 | 3 | Nothing | 3 |
4 | 6 | 3 | Break 2nd Lock | 5 |
5 | 5 | 5 | Break 3rd Lock | 7 |
The locks cannot be broken in less than 5 minutes; thus, the answer is 5.
Constraints:
n == strength.length
1 <= n <= 8
1 <= K <= 10
1 <= strength[i] <= 10^6
这个题的方法是回溯,因为问题规模较小所以回溯也不会超时。
代码如下:
1 | class Solution { |
时间复杂度:
- 对于每一个lock在回溯时都有两种选择,即选或者不选,所以时间复杂度为
O(2^N)
空间复杂度:
- 在dfs中递归调用的栈空间取决于递归的深度,深度即位列表的长度,所以需要
O(N)
的栈空间。 - 标记数组
visited
需要额外的O(N)
。 - 综合复杂度为
O(N)
2762. Continuous Subarrays
You are given a 0-indexed integer array nums
. A subarray of nums
is called continuous if:
- Let
i
,i + 1
, …,j
be the indices in the subarray. Then, for each pair of indicesi <= i1, i2 <= j
,0 <= |nums[i1] - nums[i2]| <= 2
.
Return the total number of *continuous* subarrays.
A subarray is a contiguous non-empty sequence of elements within an array.
Example 1:
1 | Input: nums = [5,4,2,4] |
Example 2:
1 | Input: nums = [1,2,3] |
Constraints:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
因为题目中说要找到某个范围内符合条件的子数组,所以自然而然的能想到应该用滑动窗口来解决这个问题。
一般的滑动窗口问题采用滑动窗口 + 一个单调队列或堆就可以解决,但由于题目中要求范围内的数的差的绝对值小于等于2,所以只用一个是不能满足要求的,因为需要同时满足范围内的最大值和最小值都满足要求,所以需要两个单调队列或者一个最大堆和最小堆。
代码如下:
1 | // heap版 |
时间复杂度:
- 每个元素最多进出堆一次,每次进出堆之后维护堆的复杂度为
O(logN)
,N个元素复杂度为O(NlogN)
空间复杂度:
- 每个堆最多存储N个元素,复杂度为
O(N)
1 | // 单调队列版 |
时间复杂度:
- 每个元素最多进出队列各一次,复杂度为
O(N)
空间复杂度:
- 每个队列最多存储N个元素,复杂度为
O(N)
1734. Decode XORed Permutation
There is an integer array perm
that is a permutation of the first n
positive integers, where n
is always odd.
It was encoded into another integer array encoded
of length n - 1
, such that encoded[i] = perm[i] XOR perm[i + 1]
. For example, if perm = [1,3,2]
, then encoded = [2,1]
.
Given the encoded
array, return the original array perm
. It is guaranteed that the answer exists and is unique.
Example 1:
1 | Input: encoded = [3,1] |
Example 2:
1 | Input: encoded = [6,5,4,6] |
Constraints:
3 <= n < 10^5
n
is odd.encoded.length == n - 1
因为每一个encoded都是从两个相邻的数得来的,所以只要知道任何一个perm就可以知道所有其他的perm。
推导过程如下:
a0 ^ a1 ^ … ^ an = 1 ^ 2 ^ … ^ n
a1 ^ a2 ^ … ^ an = e1 ^ e3 ^ … ^ en - 1
所以 a0 = e1 ^ e3 ^ … ^ en - 1 ^ 1 ^ 2 ^ … ^ n
代码如下:
1 | class Solution { |
时间复杂度:
O(n)
空间复杂度:
O(1)
1930. Unique Length-3 Palindromic Subsequences
Given a string s
, return the number of unique palindromes of length three that are a subsequence of s
.
Note that even if there are multiple ways to obtain the same subsequence, it is still only counted once.
A palindrome is a string that reads the same forwards and backwards.
A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.
- For example,
"ace"
is a subsequence of"abcde"
.
Example 1:
1 | Input: s = "aabca" |
Example 2:
1 | Input: s = "adc" |
Example 3:
1 | Input: s = "bbcbaba" |
Constraints:
3 <= s.length <= 10^5
s
consists of only lowercase English letters.
因为要求的只是长度为3的回文序列,所以只需要找到相同的两个字符然后统计中间包含的不同的字符就可以了。
比如abcba,对于a,我们只需要找到两个a的位置,然后统计夹在中间的字符数量。关键问题在于去重。
类似前缀和,采用一个二维数组来记录每个位置所有字母出现的次数,这样就可以当确定某个区间的时候知道每个字符出现的频率。
代码如下:
1 | class Solution { |
时间复杂度:
O(n)
空间复杂度:
O(n)
2381. Shifting Letters II
You are given a string s
of lowercase English letters and a 2D integer array shifts
where shifts[i] = [starti, endi, directioni]
. For every i
, shift the characters in s
from the index starti
to the index endi
(inclusive) forward if directioni = 1
, or shift the characters backward if directioni = 0
.
Shifting a character forward means replacing it with the next letter in the alphabet (wrapping around so that 'z'
becomes 'a'
). Similarly, shifting a character backward means replacing it with the previous letter in the alphabet (wrapping around so that 'a'
becomes 'z'
).
Return the final string after all such shifts to s
are applied.
Example 1:
1 | Input: s = "abc", shifts = [[0,1,0],[1,2,1],[0,2,1]] |
Example 2:
1 | Input: s = "dztz", shifts = [[0,0,0],[1,1,1]] |
Constraints:
1 <= s.length, shifts.length <= 5 * 10^4
shifts[i].length == 3
0 <= starti <= endi < s.length
0 <= directioni <= 1
s
consists of lowercase English letters.
如果直接模拟,问题的规模是5 * 10 ^ 4,最坏复杂度是O(n^2)
,绝对会超时。
可以用一个数组统计每个位置需要移动的次数,类似前缀和,这样复杂度就降到了线性。
代码如下:
1 | class Solution { |
时间复杂度:
O(n)
空间复杂度:
O(n)
983. Minimum Cost For Tickets
You have planned some train traveling one year in advance. The days of the year in which you will travel are given as an integer array days
. Each day is an integer from 1
to 365
.
Train tickets are sold in three different ways:
- a 1-day pass is sold for
costs[0]
dollars, - a 7-day pass is sold for
costs[1]
dollars, and - a 30-day pass is sold for
costs[2]
dollars.
The passes allow that many days of consecutive travel.
- For example, if we get a 7-day pass on day
2
, then we can travel for7
days:2
,3
,4
,5
,6
,7
, and8
.
Return the minimum number of dollars you need to travel every day in the given list of days.
Example 1:
1 | Input: days = [1,4,6,7,8,20], costs = [2,7,15] |
Example 2:
1 | Input: days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15] |
Constraints:
1 <= days.length <= 365
1 <= days[i] <= 365
days
is in strictly increasing order.costs.length == 3
1 <= costs[i] <= 1000
线性规划
1 | class Solution { |
时间复杂度:
O(n)
空间复杂度:
O(n)