101 Logo
onenoughtone

Solution Approach

Solution Overview

There are 3 main approaches to solve this problem:

  1. Brute Force Approach - Complex approach
  2. Dynamic Programming Approach - Complex approach
  3. Min Heap Approach - Complex approach

Approach 1: Brute Force Approach

Let's start by understanding the problem: we need to find the nth ugly number, where an ugly number is a positive integer whose prime factors are limited to 2, 3, and 5.

Thinking Process: The most straightforward approach would be to check each positive integer in sequence, determine if it's an ugly number, and count until we reach the nth one.

To check if a number is ugly, we can repeatedly divide it by 2, 3, and 5 as long as it's divisible. If the result becomes 1, then the number is ugly; otherwise, it's not.

Intuition: This approach directly follows the definition of ugly numbers. However, it's inefficient because we need to check many numbers that aren't ugly, and the checking process itself can be time-consuming for large numbers.

Algorithm:

  1. Initialize a counter to 0 and a number to 1.
  2. While the counter is less than n:
  3. a. Check if the current number is ugly.
  4. b. If it is, increment the counter.
  5. c. If the counter equals n, return the current number.
  6. d. Increment the number.
  7. To check if a number is ugly:
  8. a. While the number is divisible by 2, divide it by 2.
  9. b. While the number is divisible by 3, divide it by 3.
  10. c. While the number is divisible by 5, divide it by 5.
  11. d. If the result is 1, the number is ugly; otherwise, it's not.

Time Complexity:

O(n * log(m))

Where n is the input and m is the value of the nth ugly number. We need to check up to m numbers, and each check takes O(log m) time in the worst case.

Space Complexity:

O(1)

We only need a constant amount of space to store the counter and the current number.

Approach 2: Dynamic Programming Approach

We can optimize the brute force approach by generating ugly numbers directly, rather than checking each number.

Thinking Process: The key insight is that every ugly number (except 1) can be generated by multiplying a previous ugly number by either 2, 3, or 5. So, we can generate the sequence of ugly numbers by multiplying the existing ugly numbers by 2, 3, and 5, and taking the smallest one each time.

To avoid duplicates, we need to keep track of which previous ugly numbers have been multiplied by 2, 3, and 5. We can use three pointers to track this.

Intuition: This approach is much more efficient because we directly generate the ugly numbers in order, without checking any non-ugly numbers. The key insight is to maintain three pointers to track which previous ugly numbers to multiply by 2, 3, and 5.

Algorithm:

  1. Initialize an array dp of size n to store the ugly numbers, with dp[0] = 1.
  2. Initialize three pointers p2, p3, p5 to 0, representing the position of the ugly number to be multiplied by 2, 3, and 5 respectively.
  3. For i from 1 to n-1:
  4. a. Calculate the next potential ugly number by multiplying dp[p2] by 2, dp[p3] by 3, and dp[p5] by 5.
  5. b. Set dp[i] to the minimum of these three values.
  6. c. If dp[i] equals dp[p2] * 2, increment p2.
  7. d. If dp[i] equals dp[p3] * 3, increment p3.
  8. e. If dp[i] equals dp[p5] * 5, increment p5.
  9. Return dp[n-1].

Time Complexity:

O(n)

We perform a constant amount of work for each of the n ugly numbers we generate.

Space Complexity:

O(n)

We need to store all n ugly numbers in the dp array.

Approach 3: Min Heap Approach

Another approach is to use a min heap to efficiently find the next ugly number in the sequence.

Thinking Process: Similar to the dynamic programming approach, we know that every ugly number (except 1) can be generated by multiplying a previous ugly number by either 2, 3, or 5. We can use a min heap to keep track of all potential next ugly numbers.

We start with 1 in the heap. At each step, we extract the minimum value from the heap (which is the next ugly number), and add its multiples by 2, 3, and 5 to the heap. To avoid duplicates, we can use a set to keep track of numbers we've already added to the heap.

Intuition: The min heap approach is another efficient way to generate ugly numbers in order. The key insight is to use a min heap to always extract the smallest potential next ugly number, and to use a set to avoid duplicates.

Algorithm:

  1. Initialize a min heap with 1.
  2. Initialize a set to keep track of numbers we've already added to the heap.
  3. For i from 1 to n:
  4. a. Extract the minimum value from the heap (which is the next ugly number).
  5. b. For each factor in [2, 3, 5], calculate the product of the extracted value and the factor.
  6. c. If the product is not in the set, add it to both the set and the heap.
  7. Return the nth extracted value.

Time Complexity:

O(n log n)

We perform n heap operations, each taking O(log n) time in the worst case.

Space Complexity:

O(n)

We need to store up to 3n elements in the heap and set in the worst case.

Pseudocode

solution.pseudo
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
function nthUglyNumber(n):
count = 0
num = 1
while count < n:
if isUgly(num):
count++
if count == n:
return num
num++
return -1 // This should never be reached
function isUgly(num):
if num <= 0:
return false
while num % 2 == 0:
num /= 2
while num % 3 == 0:
num /= 3
while num % 5 == 0:
num /= 5
return num == 1
ProblemSolutionCode
101 Logo
onenoughtone