-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathonline-stock-span.py
125 lines (111 loc) · 3.79 KB
/
online-stock-span.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
# 901. Online Stock Span
# 🟠 Medium
#
# https://leetcode.com/problems/online-stock-span/
#
# Tags: Stack - Design - Monotonic Stack - Data Stream
import timeit
from sortedcontainers import SortedList
class NaiveStockSpanner:
def __init__(self):
self.prices = []
def next(self, price: int) -> int:
self.prices.append(price)
span = 0
for i in range(len(self.prices) - 1, -1, -1):
if self.prices[i] <= price:
span += 1
continue
break
return span
# Use a monotonic stack, each element contains the price and the number
# of consecutive days at which the stock was at a price not greater than
# that price on that day. When we add a new element, we start the count
# of days at one, the current day, then check the stack and pop any
# elements where the price is smaller adding the number of consecutive
# days at, or below, that price, to the current count. Then add the
# current price and result to the stack and return the result.
#
# Time complexity: O(n) - Average cost for the next method is O(1), some
# calls may be more expensive, but the total calls to stack.append and
# stack pop will be n where n each is the number of calls to next.
# Space complexity: O(n) - The stack may hold all prices if they are
# non-increasing.
#
# Runtime: 923 ms, faster than 58.78%
# Memory Usage: 19.4 MB, less than 70.93%
class StockSpanner:
def __init__(self):
# A monotonic decreasing stack where the entries are
# (val, count), the value in the stack and the count of values
# equal to, or smaller than, itself that the value has
# contiguously to its left.
self.prices = []
def next(self, price: int) -> int:
count = 1
while self.prices and self.prices[-1][0] <= price:
# Pop the tuple of (price, count of lesser to its left)
count += self.prices.pop()[1]
self.prices.append((price, count))
return count
# Your StockSpanner object will be instantiated and called as such:
# obj = StockSpanner()
# param_1 = obj.next(price)
def test():
executors = [
NaiveStockSpanner,
StockSpanner,
]
tests = [
[
[
"StockSpanner",
"next",
"next",
"next",
"next",
"next",
"next",
"next",
],
[[], [100], [80], [60], [70], [60], [75], [85]],
[None, 1, 1, 1, 2, 1, 4, 6],
],
[
[
"StockSpanner",
"next",
"next",
"next",
"next",
"next",
"next",
"next",
"next",
"next",
"next",
],
[[], [28], [14], [28], [35], [46], [53], [66], [80], [87], [88]],
[None, 1, 1, 3, 4, 5, 6, 7, 8, 9, 10],
],
]
for executor in executors:
start = timeit.default_timer()
for _ in range(1):
for col, t in enumerate(tests):
sol = executor()
for i in range(1, len(t[0])):
call = t[0][i]
result = getattr(sol, call)(t[1][i][0])
exp = t[2][i]
assert result == exp, (
f"\033[93m» {result} <> {exp}\033[91m for"
+ f" test {col} assertion {i} "
+ f"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()