文章目录
114 Gas Station 加油站
沿着一条圆形路线有 n 个加油站。你会得到两个整数阵列的gas和cost,其中:
- gas[i] 是第 i 个站点的 gas 量。
- cost[i] 是从第 i 个加油站到第 (i + 1) 个加油站所需的汽油量。(最后一个站连接到第一个站)
您有一辆可以储存无限量汽油的汽车,但您在其中一个加油站以空油箱开始旅程。
返回起始加油站的索引,以便您可以按顺时针方向绕电路行驶一次。如果不可能,则返回 -1。
可以保证最多存在一个解决方案。
示例1:
Input: gas = [1,2,3,4], cost = [2,2,4,1]
Output: 3
说明:从第 3 站(索引 3)开始,加注 4 个单位的汽油。您的水箱 = 0 + 4 = 4
前往 0 号站。您的水箱 = 4 - 1 + 1 = 3
前往 1 号站。您的水箱 = 3 - 2 + 2 = 3
前往 2 号站。您的水箱 = 3 - 2 + 3 = 4
前往 3 号站。您的水箱 = 2 - 4 + 4 = 2
示例2:
gas = [1,2,3], cost = [2,3,2]
Output: -1
说明:您不能从 0 或 1 号站开始,因为没有足够的汽油前往下一个站。
如果您从工号 2 开始,您可以移动到工号 0,然后移动到工号 1。
在站点 1,您的水箱 = 0 + 3 - 2 + 1 - 2 = 0。
您被困在第 1 站,因此无法绕着电路行驶。
解题1: 贪婪
这里用diff数组来存储油的加入和消耗之差,来判断是否可以从该位置出发。
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
# 首先要保证汽油之和要大于消耗
if sum(gas) < sum(cost):
return -1
total = 0
res = 0
for i in range(len(gas)):
total += (gas[i] - cost[i])
if total < 0:
total = 0
res = i + 1
return res
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
115 Hand of Straights 顺子之手
您将得到一个整数数组hand和一个整数 groupSize,其中 hand [i] 是写在第i张卡片上的值。
您需要将卡片重新排列成组,以便每个组的卡片数量为 groupSize,并且卡片值连续增加 1。
如果可以通过这种方式重新排列卡片,则返回 true ,否则返回 false 。
示例1:
Input: hand = [1,2,4,2,3,5,3,4], groupSize = 4
Output: true
说明:卡片可以重新排列为 [1,2,3,4] 和 [2,3,4,5] 。
示例2:
Input: hand = [1,2,3,3,4,5,6,7], groupSize = 4
Output: false
解释:我们能得到的最接近的是 [1,2,3,4] 和 [3,5,6,7] ,但第二组中的牌不是连续的。
解题1: 堆
我们首先用HashMap来存储每个数字的个数,每使用一个就把其对应的个数减一。同时用MinHeap来弹出最小值,同时当某个数在HashMap中的个数减少为0的时候,也将该数从最小堆中弹出。
class Solution:
def isNStraightHand(self, hand: List[int], groupSize: int) -> bool:
# 首先判断是否可以整除groupSize
if len(hand) % groupSize:
return False
count = {}
for n in hand:
count[n] = 1 + count.get(n, 0)
minH = list(count.keys())
heapq.heapify(minH)
while minH:
first = minH[0]
for i in range(first, first + groupSize):
if i not in count:
return False
count[i] -= 1
if count[i] == 0:
if i != minH[0]:
return False
heapq.heappop(minH)
return True
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
116 Merge Triplets to Form Target 将 Triplelet 合并到 Form Target
你得到一个整数 triplelets 的二维数组,其中三元组[i] = [ai, bi, ci] 表示第 i 个三元组。你还会得到一个整数数组 target = [x, y, z],这是我们想要得到的三元组。
要获取 target,您可以对 triplelets 零次或多次应用以下作用:
选择两个不同的三元组 triplets[i] 和 triplets[j],并将 triplets[j] 更新为 [max(ai, aj), max(bi, bj), max(ci, cj)] 。
例如,如果三元组[i] = [1, 3, 1] 且三元组[j] = [2, 1, 2],则三元组[j]将更新为 [max(1, 2), max(3, 1), max(1, 2)] = [2, 3, 2] 。
如果可以将 target 作为三元组的元素获取,则返回 true,否则返回 false。
示例1:
Input: triplets = [[1,2,3],[7,1,1]], target = [7,2,3]
Output: true
解释:选择第一个和第二个三元组,将第二个三元组更新为 [max(1, 7), max(2, 1), max(3, 1)] = [7, 2, 3]。
示例2:
Input: triplets = [[2,5,6],[1,4,4],[5,7,5]], target = [5,4,6]
Output: false
解释:
解题1: 贪婪
class Solution:
def mergeTriplets(self, triplets: List[List[int]], target: List[int]) -> bool:
good = set()
for t in triplets:
if t[0] > target[0] or t[1] > target[1] or t[2] > target[2]:
continue
for i, v in enumerate(t):
if v == target[i]:
good.add(i)
return len(good) == 3
时间复杂度:
O
(
n
2
)
O(n²)
O(n2)
空间复杂度:
O
(
1
)
O(1)
O(1)
117 Partition Labels 分区标签
你被给了一个由小写英文字母组成的字符串 s 。
我们希望尽可能地将字符串分割成多个子字符串,同时确保每个字母最多只出现在一个子字符串中。
返回一个表示这些子字符串大小的整数列表,按其在字符串中出现的顺序排列。
示例1:
Input: s = "xyxxyzbzbbisl"
Output: [5, 5, 1, 1, 1]
解释:字符串可以被分割为 ["xyxxy", "zbzbb", "i", "s", "l"] 。
示例2:
Input: s = "abcabc"
Output: [6]
解题1: 双指针(贪心)
这里设置一个HashMap来映射每个字母最后出现的位置索引。
size用来记录substring的长度,同时用来检查改位置的字母最后一次出现的索引是不是小于end,如果不是需要修改end的值。
class Solution:
def partitionLabels(self, s: str) -> List[int]:
lastIndex = {} # char -> last index in s
for i, c in enumerate(s):
lastIndex[c] = i
res = []
size, end = 0, 0
for i, c in enumerate(s):
size += 1
end = max(end, lastIndex[c])
if i == end:
res.append(size)
size = 0
return res
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
m
)
O(m)
O(m)
n 是字符串 s 的长度,而 m 是字符串 s 中唯一字符的数量。
118 Valid Parenthesis String 有效的括号字符串
您被给定一个字符串 s ,它只包含三种类型的字符: ‘(’ 、 ‘)’ 和 ‘*’ 。
返回 true 如果 s 有效,否则返回 false 。
一个字符串如果遵循以下所有规则,则是有效的:
- 每个左括号 ‘(’ 都必须有一个相应的右括号 ‘)’ 。
- 每个右括号 ‘)’ 都必须有一个对应的左括号 ‘(’ 。
- 左括号 ‘(’ 必须在相应的右括号 ‘)’ 之前。
- 一个 ‘*’ 可以被当作一个右括号 ‘)’ 字符或一个左括号 ‘(’ 字符,或者作为一个空字符串 “” 。
示例1:
Input: s = "((**)"
Output: true
解释:其中一个 '*' 可能是一个 ')' ,另一个可能是空字符串。
示例2:
Input: s = "(((*)"
Output: false
说明:字符串无效,因为开头多了一个 '(' ,不管多出来的 '*' 。
解题1: 贪心算法
class Solution:
def checkValidString(self, s: str) -> bool:
leftMin, leftMax = 0, 0
for c in s:
if c == "(":
leftMin, leftMax = leftMin + 1, leftMax + 1
elif c == ")":
leftMin, leftMax = leftMin - 1, leftMax - 1
else:
leftMin, leftMax = leftMin - 1, leftMax + 1
if leftMax < 0:
return False
if leftMin < 0: # s = ( * ) (
leftMin = 0
return leftMin == 0
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
119 Insert Interval 插入间隔
您被给定一个非重叠区间数组 intervals ,其中 intervals[i] = [start_i, end_i] 表示 ith 区间的开始和结束时间。 intervals 最初按 start_i 升序排序。
您被赋予另一个区间 newInterval = [start, end] 。
将 newInterval 插入到 intervals 中,使得 intervals 仍然按 start_i 升序排列,并且 intervals 仍然没有重叠的区间。如果需要,可以合并重叠的区间。
返回 intervals 后添加 newInterval 。
注意:如果区间没有公共点,则它们是非重叠的。例如,[1,2] 和 [3,4] 是非重叠的,但 [1,2] 和 [2,3] 是重叠的。
示例1:
Input: intervals = [[1,3],[4,6]], newInterval = [2,5]
Output: [[1,6]]
示例2:
Input: intervals = [[1,2],[3,5],[9,10]], newInterval = [6,7]
Output: [[1,2],[3,5],[6,7],[9,10]]
解题1: 贪心算法
class Solution:
def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
res = []
for i in range(len(intervals)):
if newInterval[1] < intervals[i][0]:
res.append(newInterval)
return res + intervals[i:]
elif newInterval[0] > intervals[i][1]:
res.append(intervals[i])
else:
newInterval = [min(newInterval[0], intervals[i][0]), max(newInterval[1], intervals[i][1])]
res.append(newInterval)
return res
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
120 Merge Intervals 合并区间
给定一个数组 intervals ,其中 intervals[i] = [start_i, end_i] ,合并所有重叠的区间,并返回覆盖输入中所有区间的非重叠区间数组。
您可以将答案以任何顺序返回。
注意:如果区间没有公共点,则它们是非重叠的。例如, [1, 2] 和 [3, 4] 是非重叠的,但 [1, 2] 和 [2, 3] 是重叠的。
示例1:
Input: intervals = [[1,3],[1,5],[6,7]]
Output: [[1,5],[6,7]]
示例2:
Input: intervals = [[1,2],[2,3]]
Output: [[1,3]]
解题1: 排序
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
# O(nlogn) 首先需要从小到大进行排序
intervals.sort(key = lambda i : i[0])
output = [intervals[0]]
for start, end in intervals[1:]:
lastEnd = output[-1][1]
# 判断是否有重叠
if start <= lastEnd:
output[-1][1] = max(lastEnd, end)
else:
output.append([start, end])
return output
时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
空间复杂度:
O
(
n
)
O(n)
O(n)
121 Non-overlapping Intervals 非重叠区间
给定一个区间数组 intervals ,其中 intervals[i] = [start_i, end_i] ,返回你需要移除的最小区间数,以使得剩余的区间不重叠。
注意:即使有共同点,区间也不会重叠。例如, [1, 3] 和 [2, 4] 是重叠的,但 [1, 2] 和 [2, 3] 是非重叠的。
示例1:
Input: intervals = [[1,2],[2,4],[1,4]]
Output: 1
解释:移除[1,4]之后,其余的区间都是非重叠的。
示例2:
Input: intervals = [[1,2],[2,4]]
Output: 0
解题1: 贪心(按起始时间排序)
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
intervals.sort()
res = 0
prevEnd = intervals[0][1]
for start, end in intervals[1:]:
# 如果没有重叠
if start >= prevEnd:
prevEnd = end
else:
res += 1
prevEnd = min(end, prevEnd)
return res
时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
空间复杂度:
O
(
1
)
或
O
(
n
)
O(1) 或 O(n)
O(1)或O(n),取决于排序算法。
122 Meeting Rooms II 会议室 II
给定一个包含会议时间间隔对象的数组,这些对象包括开始和结束时间,求在不发生任何冲突的情况下安排所有会议所需的最少天数。
示例1:
Input: intervals = [(0,40),(5,10),(15,20)]
Output: 2
解释:
day1: (0,40)
day2: (5,10),(15,20)
示例2:
Input: intervals = [(4,9)]
Output: 1
解题1: 双指针
当我们进行遍历时,首先0开始,然后时5。当遍历到5的时候,发现0-30这个会议还没有结束。所以count+1。
当我们继续,到10的时候,一个会议结束,count-1。再继续,到15的时候,因为0-30这个还没有结束,所以count+1。
用双指针,每次比较start和end中的值,取小的那个,如果小的在start,那么count+1,否则count-1。
当都遇到10的时候,因为要一个会议结束,另一个才能开始。所以这里,end中的指针要+1,count要-1。
"""
Definition of Interval:
class Interval(object):
def __init__(self, start, end):
self.start = start
self.end = end
"""
class Solution:
def minMeetingRooms(self, intervals: List[Interval]) -> int:
start = sorted([i.start for i in intervals])
end = sorted([i.end for i in intervals])
res, count = 0, 0
s, e = 0, 0
# 只要遍历完satrt就可以停止
while s < len(intervals):
if start[s] < end[e]:
s += 1
count += 1
else:
e += 1
count -= 1
res = max(res, count)
return res
时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
空间复杂度:
O
(
n
)
O(n)
O(n)
123 Rotate Image 旋转图片
给定一个整数矩阵 n x n ,将其顺时针旋转 90 度。
您必须在原地旋转矩阵。不要分配另一个二维矩阵进行旋转。
示例1:
Input: matrix = [
[1,2],
[3,4]
]
Output: [
[3,1],
[4,2]
]
示例2:
Input: matrix = [
[1,2,3],
[4,5,6],
[7,8,9]
]
Output: [
[7,4,1],
[8,5,2],
[9,6,3]
]
解题1: 旋转四格
从上图可以看出,一个n×n的矩阵,最外层旋转的次数不是n,而是n-1。
已经知道移动的步骤了,为了减少操作次数,我们可以先用一个临时变量来存储左上角的数,然后顺时针移动数字。
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
l, r = 0, len(matrix) - 1
while l < r:
for i in range(r - l):
top, bottom = l, r
# 保存左上角
topLeft = matrix[top][l + i]
# 左下移动到左上
matrix[top][l + i] = matrix[bottom - i][l]
# 右下到左下
matrix[bottom - i][l] = matrix[bottom][r - i]
# 右上到右下
matrix[bottom][r - i] = matrix[top + i][r]
# 左上到右上
matrix[top + i][r] = topLeft
r -= 1
l += 1
时间复杂度:
O
(
n
2
)
O(n²)
O(n2)
空间复杂度:
O
(
1
)
O(1)
O(1)