Skip to content

Commit 62a7f11

Browse files
author
Hamid Gasmi
committed
#227 is completed
1 parent f7fbfdb commit 62a7f11

File tree

1 file changed

+164
-0
lines changed

1 file changed

+164
-0
lines changed

09-problems/lc_264_ugly_number_ii.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import heapq
2+
3+
class Solution:
4+
"""
5+
This solution is implemented with a Dynamic Programming approach + bottom up:
6+
n-th ugly number = min(n previous ugly numbers)
7+
8+
Ugly numbers are stored in 3 lists: ugly_nbrs_2, ugly_nbrs_3, ugly_nbrs_5
9+
A list i contains all ugly numbers computed by multiplying a previous ugly number by i
10+
11+
the min is computed by the min of the 1st unseen number from each list
12+
13+
"""
14+
15+
# Time Complexity: O(n)
16+
# Space Complexity: O(n)
17+
def nthUglyNumber(self, n: int) -> int:
18+
19+
if n == 0:
20+
return n
21+
22+
ugly_nbrs_2 = [2]
23+
ugly_nbrs_3 = [3]
24+
ugly_nbrs_5 = [5]
25+
26+
# Indexes to keep track of unseen numbers
27+
idx_2 = 0
28+
idx_3 = 0
29+
idx_5 = 0
30+
31+
# prev_ugly_nbr is to skip duplicate values
32+
ugly_nbr = 1
33+
prev_ugly_nbr = 1
34+
while n > 1:
35+
36+
ugly_nbr = min(ugly_nbrs_2[idx_2], ugly_nbrs_3[idx_3], ugly_nbrs_5[idx_5])
37+
if ugly_nbr == ugly_nbrs_2[idx_2]:
38+
idx_2 += 1
39+
40+
elif ugly_nbr == ugly_nbrs_3[idx_3]:
41+
idx_3 += 1
42+
43+
else:
44+
idx_5 += 1
45+
46+
if ugly_nbr == prev_ugly_nbr:
47+
continue
48+
49+
ugly_nbrs_2.append(ugly_nbr * 2)
50+
ugly_nbrs_3.append(ugly_nbr * 3)
51+
ugly_nbrs_5.append(ugly_nbr * 5)
52+
53+
n -= 1
54+
prev_ugly_nbr = ugly_nbr
55+
56+
return ugly_nbr
57+
58+
class SolutionHeapQ:
59+
"""
60+
The solution is implemented with a Dynamic Programming approach + bottom up:
61+
n-th ugly number = min(n previous ugly numbers)
62+
63+
The min operation is implemented with a min-heap
64+
65+
The issue in this solution is min operation running time: O(logn)
66+
See time analysis below
67+
68+
"""
69+
# Time Complexity: T(n) = Sum(i in [1:n]) T(pop) + 3 T(push)
70+
# T(n) = (0 + 0 + 0 + log2) + (log3 + log2 + log3 + log4) + ... + log2i-1 + log(2i - 2) + log(2i - 1) + log(2i) + ...
71+
# T(n) < Sum(i in [1:n]) 4 log2i
72+
# T(n) < 4 log(2 * 4 * ... * 2i * ... 2n) < 4 log(2**n * 1 * 2 * ... * i * ... * n)
73+
# T(n) < 4n + 4 logn!
74+
# T(n) = O(n + logn!)
75+
# Space Complexity: S(n) = (3 - 1) + (3 - 1) + ... + (3 - 1) = O(n)
76+
# At each iteration, we push 3 items and we pop 1 item.
77+
def nth_ugly_number(self, n: int) -> int:
78+
79+
if n == 0:
80+
return n
81+
82+
ugly_nbrs = []
83+
heapq.heappush(ugly_nbrs, 1)
84+
85+
ugly_nbr = -1
86+
prev_ugly_nbr = -1
87+
while n > 0:
88+
89+
ugly_nbr = heapq.heappop(ugly_nbrs)
90+
if ugly_nbr == prev_ugly_nbr:
91+
continue
92+
93+
heapq.heappush(ugly_nbrs, ugly_nbr * 2)
94+
heapq.heappush(ugly_nbrs, ugly_nbr * 3)
95+
heapq.heappush(ugly_nbrs, ugly_nbr * 5)
96+
97+
n -= 1
98+
prev_ugly_nbr = ugly_nbr
99+
100+
return ugly_nbr
101+
102+
class SolutionBFS:
103+
"""
104+
Ugly numbers could be seen as a 3-ary tree which the number 1 is the root
105+
1
106+
/ | \
107+
2 3 5
108+
/ / | /|\ \ \ \
109+
4 6 10 6 9 15 10 15 25
110+
/|
111+
8 12
112+
This 3-ary tree could be traversed with a BFS approach
113+
For all node of a level (i), we compute the node of level (i + 1)
114+
115+
Even though, nodes values is increasing, they aren't sorted neither inside levels nor between levels
116+
117+
The challenge is therefore how to find the nth ugly number.
118+
119+
My solution is to store all ugly numbers sorted.
120+
I stop when I reach a length of n and the last item is less than the min ugly number visited in the current level
121+
122+
It's not efficient!
123+
I shouldn't split nodes by level.
124+
I should have all tree nodes together and get the next value as the min of all node.
125+
Solution: Min Heap.
126+
127+
"""
128+
def nth_ugly_number(self, n: int) -> int:
129+
130+
if n <= 1:
131+
return n
132+
133+
n_ugly_nbrs = [1]
134+
curr_level = [1]
135+
while n > 0:
136+
137+
next_level = []
138+
curr_min_ugly = math.inf
139+
for ugly_n in curr_level:
140+
141+
next_ugly = ugly_n * 2
142+
if not next_ugly in next_level:
143+
next_level.append(next_ugly)
144+
curr_min_ugly = min(curr_min_ugly, next_ugly)
145+
146+
next_ugly = ugly_n * 3
147+
if not next_ugly in next_level:
148+
next_level.append(next_ugly)
149+
curr_min_ugly = min(curr_min_ugly, next_ugly)
150+
151+
next_ugly = ugly_n * 5
152+
if not next_ugly in next_level:
153+
next_level.append(next_ugly)
154+
curr_min_ugly = min(curr_min_ugly, next_ugly)
155+
156+
curr_level = next_level
157+
n_ugly_nbrs.extend(next_level)
158+
n_ugly_nbrs.sort()
159+
if len(n_ugly_nbrs) >= n and n_ugly_nbrs[n - 1] <= curr_min_ugly:
160+
break
161+
162+
return n_ugly_nbrs[n - 1]
163+
164+

0 commit comments

Comments
 (0)