-
Notifications
You must be signed in to change notification settings - Fork 4
/
jump-game-vi.py
132 lines (113 loc) · 4.58 KB
/
jump-game-vi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# 1696. Jump Game VI
# 🟠 Medium
#
# https://leetcode.com/problems/jump-game-vi/
#
# Tags: Array - Dynamic Programming - Queue - Sliding Window
# - Heap (Priority Queue) - Monotonic Queue
import timeit
from heapq import heappop, heappush
from typing import List
# We can visit each element of nums, for each, calculate the best way to
# reach it from the k elements from which we can jump to this one. To
# avoid having to visit the k elements in each iteration, we can use a
# sliding window to record the best value of the possible ones, on each
# step, we add the value under the right pointer to the pool and remove
# the value under the left pointer.
#
# Time complexity: O(n*log(n)) - We visit each element and, for each,
# push into the heap at O(log(n)). We have no guarantee that the heap
# will stay under size k, it would only if the max element was coming
# out of the sliding window. If the max element gets replaced as the
# top of the heap, it will stay in. The heap could grow to size n.
# Space complexity; O(n) - Same as before, the heap could grow to the
# same size as the input list.
#
# Runtime: 2338 ms, faster than 14.21% of Python3 online submissions for
# Jump Game VI.
# Memory Usage: 33.5 MB, less than 30.88% of Python3 online submissions
# for Jump Game VI.
class ListAndHeap:
def maxResult(self, nums: List[int], k: int) -> int:
# If we don't have any values, the max will be 0.
if not nums:
return 0
heap = []
for i, num in enumerate(nums):
# Base case, we start at index 0, preserve the value and
# push it into the heap.
if i == 0:
heappush(heap, (-num, i))
continue
else:
# Pop the top element if it is left of the start of the
# sliding window of values we can use.
while heap[0][1] < i - k:
heappop(heap)
# Update the current position with the most gain we can
# obtain jumping here.
nums[i] -= heap[0][0]
# Push the current value into the heap.
heappush(heap, (-nums[i], i))
return nums[-1]
# Looking at the previous solution, we can see that we are updating the
# list but only using its value to push into the heap, we can simplify
# the solution by using the heap, instead of nums, as our dp.
#
# Time complexity: O(n*log(n)) - We update the heap for each value of
# the input array.
# Space complexity: O(n) - for the heap.
#
# Runtime: 1843 ms, faster than 40.83% of Python3 online submissions for
# Jump Game VI.
# Memory Usage: 33.3 MB, less than 34.29% of Python3 online submissions
# for Jump Game VI.
class Heap:
def maxResult(self, nums: List[int], k: int) -> int:
# If we don't have any values, the max will be 0.
if not nums:
return 0
# Base case, we only have one element.
result = nums[0]
# Push the first value into the heap.
heap = [(-nums[0], 0)]
for i in range(1, len(nums)):
# Pop the top element if it is left of the start of the
# sliding window of values we can use.
while heap[0][1] < i - k:
heappop(heap)
# Update the current position with the most gain we can
# obtain jumping here.
result = nums[i] - heap[0][0]
# Push the current value into the heap.
heappush(heap, (-result, i))
return result
# TODO add a monotonic deque solution, something similar to this:
# https://leetcode.com/problems/jump-game-vi/discuss/1260696/Python-Monotonic-deque-explained
def test():
executors = [ListAndHeap, Heap]
tests = [
[[-1], 2, -1],
[[-1, -1], 200, -2],
[[1, -1, -2, 4, -7, 3], 2, 7],
[[10, -5, -2, 4, 0, 3], 3, 17],
[[1, -5, -20, 4, -1, 3, -6, -3], 2, 0],
[[1, -5, -20, 4, -1, 3, -6, -3], 20, 5],
]
for executor in executors:
start = timeit.default_timer()
for _ in range(1):
for col, t in enumerate(tests):
sol = executor()
result = sol.maxResult([*t[0]], t[1])
exp = t[2]
assert result == exp, (
f"\033[93m» {result} <> {exp}\033[91m for"
+ f" test {col} using \033[1m{executor.__name__}"
)
stop = timeit.default_timer()
used = str(round(stop - start, 5))
cols = "{0:20}{1:10}{2:10}"
res = cols.format(executor.__name__, used, "seconds")
print(f"\033[92m» {res}\033[0m")
test()