解析
53. Maximum Subarray
- 不断贪心的找最大的当前和
- 如果之前的和小于0,就重置 cur_sum 成 0 ,因为 小于 0 的和并不能 contribute 任何。因而从当前元素开始重新考虑
- 如果之前的和大于0,就继续累加当前元素以得出 cur_sum。
- 再把 cur_sum 和 res 比较去大值
# brute force
# - time: O(n^2)
# - space: O(1)
def maxSubArray(self, nums: List[int]) -> int:
n, res = len(nums), nums[0]
for i in range(n):
cur = 0
for j in range(i, n):
cur += nums[j]
res = max(res, cur)
return res
# greedy
# 也有一点像 sliding window,即 if cur_sum < 0, shrink the window to start from the current. Otherwise add to cur_sum
# - time: O(n)
# - space: O(1)
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
res = nums[0]
curSum = nums[0]
for i in range(1, len(nums)):
num = nums[i]
# If cur_sum < 0, means the previous total sum doesn't help, and thus reset cur_sum to be current num.
if curSum < 0:
curSum = 0
curSum += num
res = max(res, curSum)
return res
ref
55. Jump Game
- 先把 goal 设置在最右边的索引
- 不断向左循环,看看能不能从当前位置跳到 goal(索引),如果可以,把当前位置设置为 goal(因为当前位置可以跳到 goal)
- 最后看看当前的 goal 是不是为0,因为为0表示当前的goal就是起跳的位置
# brute force
# - time: O(n^n)
...
# DP(top-down)
# - time: O(n^2)???
# - space: O(n)
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
dp = {}
def dfs(i):
if i >= len(nums) - 1:
return True
if i in dp:
return dp[i]
if nums[i] == 0:
return False
for step in range(1, nums[i] + 1):
if dfs(i + step):
dp[i] = True
return True
dp[i] = False
return False
return dfs(0)
# greedy - myself
# - time: O(n)
# - space: O(1)
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
goal = len(nums) - 1
i = len(nums) - 1
while i >= 0:
if i + nums[i] >= goal:
# 如果当前位置可以跳到 goal,则设当前位置(一个更近的位置)为 goal
goal = i
i -= 1
else:
i -= 1
return goal == 0 # goal == 0意味着从起点是一个 goal,因而起点能 reach goal
# greedy - leetcode
# - time: O(n)
# - space: O(1)
def canJump(self, nums: List[int]) -> bool:
goal = len(nums) - 1
for i in range(len(nums) - 1, -1, -1):
if i + nums[i] >= goal:
goal = i
return goal == 0
ref
45. Jump Game II
- 对于每一个位置,跳之前,遍历其可能的所有跳到的每个位置的索引,记下最大的索引,并跳到这里
- 有点像BFS
def jump(self, nums: List[int]) -> int:
l, r = 0, 0
count = 0
while r < len(nums) - 1: # before r reaches the last index, continue
farthest = 0
for i in range(l, r + 1):
farthest = max(farthest, i + nums[i])
l = r + 1 # because it is guaranteed that I can rach the last index
r = farthest
count += 1
return count
# DP(top-down)
# - time: O(n^2)
# - space: O(n)
def jump(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
dp = {}
def dfs(i):
if i >= len(nums) - 1:
return 0
if i in dp:
return dp[i]
if nums[i] == 0:
return 100000
minStep = 100000
for step in range(1, nums[i] + 1):
minStep = min(minStep, dfs(i + step) + 1)
dp[i] = minStep
return minStep
return dfs(0)
ref
134. Gas Station
- 不太好理解
# brute force
# - time: O(n^2)
# - space: O(1)
# greedy
# - time: O(n)
# - space: O(1)
def canCompleteCircuit(self, gas, cost):
"""
:type gas: List[int]
:type cost: List[int]
:rtype: int
"""
if sum(gas) < sum(cost):
# If this case, we are not able to finish the route in any ways, because the total gas is not enough
return -1
# If comes to here, we guarantee that we are able to finish the circular route, because sum(cost) >= sum(gas)
left = 0
start = 0
for idx in range(len(gas)):
left += gas[idx] - cost[idx]
if left < 0:
# 如果剩的 < 0,把下一个节点视为起始节点
start = idx + 1
left = 0
# Because every time we pass every gas station and left >= 0, and sum(cost) <= sum(gas), so the gas needed for the gas stations that we haven't gone through will be < than the gas left.
return start
ref
678. Valid Parenthesis String
- 这题好难,暂时跳过了
def checkValidString(self, s: str) -> bool:
# ( * ) * ( )
minLeft = 0
maxLeft = 0
# space: O(1)
# time: O(n)
for char in s:
if char == "(":
minLeft += 1
maxLeft += 1
elif char == ")":
minLeft -= 1
maxLeft -= 1
else:
minLeft -= 1
maxLeft += 1
if maxLeft < 0: # e.g., ))((
return False
if minLeft < 0: # we need this if because of s = ( * ) ( case
minLeft = 0
return minLeft == 0
ref
763. Partition Labels
- 先用一个 map 存储每个字符出现索引的最大值
- 贪心的去比较并更新 curPartitionMaxEndIdx
- 如果 idx == curPartitionMaxEndIdx(说明到了这个 partition 的结束位置),因而把 当前 size append 到res,再重置 size = 0
# greedy + hashmap
# - time: O(n)
# - space: O(1) # O(26)
def partitionLabels(self, s):
"""
:type s: str
:rtype: List[int]
"""
lastIdxMap = {} # char -> lastIndexOfThisChat map. Space: O(1)
for i, char in enumerate(s):
lastIdxMap[char] = i
res =[]
curPartitionMaxEndIdx,size = 0,0
for idx, char in enumerate(s):
curPartitionMaxEndIdx = max(lastIdxMap[char], curPartitionMaxEndIdx)
size += 1
if idx == curPartitionMaxEndIdx :
# 扫描到当前 partition 的最右端索引时,把当前 partition 的长度加到 res,并重置 size
res.append(size)
size, curPartitionMaxEndIdx = 0, 0 # 更新 maxEndIdx 不是必须的
return res
ref
846. Hand of Straights
- 不难
- 构造 hashmap + minHeap
- use min heap to get the min element by O(1)
- 用 hashmap 来存储当前未处理的每个元素的数量
- get the element starting from the min, until we have the corresponding group
# greedy + hashmap + heap - myself
# - time: O(n * logn)
# - space: O(n)
def isNStraightHand(self, hand, groupSize):
"""
:type hand: List[int]
:type groupSize: int
:rtype: bool
"""
if len(hand) % groupSize != 0:
return False
m = {} # space: O(n)
for n in hand:
m[n] = m.get(n, 0) + 1
minHeap = [] # space: O(n), 用 minHeap 可以以每次 O(1) 取出最小值
for n in m.keys():
minHeap.append(n) # 不需要保存 count
heapq.heapify(minHeap)
# print(m)
while minHeap: # time: O(n * logn)
smallest = minHeap[0] # 取出当前最小的元素
# 依次检查其对应的 group
for n in range(smallest, smallest + groupSize):
if n not in m:
return False
m[n] -= 1
if m[n] == 0:
del m[n] # Not necessary to have this line
heapq.heappop(minHeap)
return True
# greedy + hashmap + heap - leetcode
# - time: O(n * logn)
# - space: O(n)
def isNStraightHand(self, hand: List[int], groupSize: int) -> bool:
if len(hand) % groupSize != 0:
return False
m = {}
for ele in hand:
m[ele] = m.get(ele, 0) + 1
minHeap = list(m.keys())
heapq.heapify(minHeap)
while minHeap:
cur_min = minHeap[0]
for i in range(cur_min, cur_min + groupSize):
if i not in m:
return False
m[i] -= 1
if m[i] == 0:
if i != minHeap[0]:
# 如果当前扫描的值不是 heap 中的最小值,直接可以 return False
return False
heapq.heappop(minHeap)
return True
ref
1296. Divide Array in Sets of K Consecutive Numbers
1899. Merge Triplets to Form Target Triplet
- 解法比较 smart
- 任何 triplet 中如果出现了任何一个元素 大于在 target 里的对应索引元素,则一定不考虑该 triplet
- 比如 [1, 8, 4], target 为 [2, 7, 5],则一定不考虑该 triplet,因为如果考虑,一定会出现 [*, 8, *]
- 如果不满足上述条件,则说明当前 triplet 的任何一个元素 都小于等于 target 里的对应索引元素
- 因此,如果有等于的情况,
# greedy
# - time: O(n)
# - space: O(1)
def mergeTriplets(self, triplets, target):
"""
:type triplets: List[List[int]]
:type target: List[int]
:rtype: bool
"""
hasTargetX, hasTargetY, hasTargetZ = False, False, False
for triplet in triplets:
if triplet[0] > target[0] or triplet[1] > target[1] or triplet[2] > target[2]:
# Not consider such a triplet, since any of elements of this triplet is > than the corrsponding element in the target array,
# we shouldn't consider this triplet, otherwise, we cannot form target if we use this triplet, e.g., triplet: [2,8,3] target: [2,7,3] -> the final triplet will be [*,8,*]
continue
if triplet[0] == target[0]:
hasTargetX = True
if triplet[1] == target[1]:
hasTargetY = True
if triplet[2] == target[2]:
hasTargetZ = True
return hasTargetX and hasTargetY and hasTargetZ
ref
- https://www.youtube.com/watch?v=kShkQLQZ9K4
- https://neetcode.io/problems/merge-triplets-to-form-target