[Notes] Greedy

Posted by 西维蜀黍的OJ Blog on 2023-09-08, Last Modified on 2024-02-01

解析

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

ref

53. Maximum Subarray

  • 不断贪心的找最大的当前和
    • 如果之前的和小于0,就重置 cur_sum 成 0 ,因为 小于 0 的和并不能contribute任何。然后考虑当前元素
    • 如果之前的和大于0,就继续考虑(累加)当前元素
# 写法 1
	def maxSubArray(self, nums: List[int]) -> int:
		cur_max = nums[0]
		cur_sum = 0
		for num in nums:
			if cur_sum < 0:
				cur_sum = 0
				
			cur_sum += num
			cur_max = max(cur_max, cur_sum)

		return cur_max

# 写法 2
	def maxSubArray(self, nums: List[int]) -> int:
		cur_max_sum = nums[0]
		cur_sum = cur_max_sum
		for i in range(1, len(nums)):
			num = nums[i]
			if cur_sum > 0:
				cur_sum += num
			else:  # the previous sum doesn't help, and thus reset cur_sum to be current num. 0 doesn't help, and thus the condition is > 0
				cur_sum = num
			cur_max_sum = max(cur_max_sum, cur_sum)

		return cur_max_sum

ref

55. Jump Game

  • 先把 goal 设置在最右边的位置
  • 不断向左循环,看看能不能从当前位置跳到 goal,如果可以,把当前位置设置为 goal(因为当前位置可以跳到 goal)
  • 最后看看当前的 goal是不是为0,为0表示当前的goal就是起跳的位置
# solution 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

# solution 2
def canJump(self, nums: List[int]) -> bool:
		l = 0
		r = 0 + nums[0]
		while True:
			cur_l = l
			cur_r = r
			for i in range(cur_l, cur_r + 1):
				r = max(r, i + nums[i])
				if r >= len(nums) - 1:
					return True
			if r == cur_r:
				return False
			l = cur_r + 1

ref

134. Gas Station

  • time: O(n), space: O(1)
	def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
		if sum(gas) < sum(
				cost):  # If this case, we know that we are not able to finish the route in any ways, because we know that the total gas is not enough
			return -1

		# If comes to here, we guarantee that we are able to finish the circular route
		left = 0
		res = 0
		for idx in range(len(gas)):
			left += gas[idx] - cost[idx]
			if left < 0:
				res = idx + 1
				left = 0
		# Because every time we pass every gas station and left >= 0, and sum(cost) <= sum(gas), so the pgas needed for the gas stations that we haven't gone through will be < than the left.
		return res

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

  • 贪心的去比较 max_last_idx
	def partitionLabels(self, s: str) -> List[int]:
		# get the last index of every element
		lastIndex = {}
		for idx in range(len(s)):
			char = s[idx]
			lastIndex[char] = idx

		res = []
		max_last_idx = 0
		size = 0
		for idx in range(len(s)):
			char = s[idx]

			max_last_idx = max(max_last_idx, lastIndex[char])
			size += 1
			if idx == max_last_idx:  # reach the last index of all elements that we have seen
				res.append(size)
				size = 0  # reset the size

		return res

ref

846. Hand of Straights

  • use min heap to get the min clementi by O(1)
  • get the element starting from the min, until we have groupSize elements
	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]:
						return False
					heapq.heappop(minHeap)
		return True

ref

1899. Merge Triplets to Form Target Triplet

	def mergeTriplets(self, triplets: List[List[int]], target: List[int]) -> bool:
		hasX = False
		hasY = False
		hasZ = 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 [x,8,x]
				continue

			# Then, for all triplets come to here, the element will <= the element of the target, and if = for all three elements, we are sure that we can make it somehow
			if triplet[0] == target[0]:
				hasX = True
			if triplet[1] == target[1]:
				hasY = True
			if triplet[2] == target[2]:
				hasZ = True

		return hasX and hasY and hasZ

ref

Ref