The Indian Engineer

Problem 264 Ugly Number II

Posted on 4 mins

Math Min-Heap Priority-Queue

Problem Statement

Link - Problem 264

Question

An ugly number is a positive integer whose prime factors are limited to 2, 3, and 5.

Given an integer n, return the nth ugly number.

Example 1

Input: n = 10
Output: 12
Explanation: [1, 2, 3, 4, 5, 6, 8, 9, 10, 12] is the sequence of the first 10 ugly numbers.

Example 2

Input: n = 1
Output: 1
Explanation: 1 has no prime factors, therefore all of its prime factors are limited to 2, 3, and 5.

Constraints

- `1 <= n <= 1690`

Solution

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int> primes = {2, 3, 5};
        priority_queue<long, vector<long>, greater<long>> uglyHeap;
        unordered_set<long> visited;

        uglyHeap.push(1);
        visited.insert(1);

        long curr;
        for (int i = 0; i < n; ++i) {
            curr = uglyHeap.top();
            uglyHeap.pop();
            for (int prime : primes) {
                long new_ugly = curr * prime;
                if (visited.find(new_ugly) == visited.end()) {
                    uglyHeap.push(new_ugly);
                    visited.insert(new_ugly);
                }
            }
        }
        return (int)curr;
    }
};

Complexity Analysis

| Algorithm | Time Complexity | Space Complexity |
| --------- | --------------- | ---------------- |
| Min Heap  | O(nlogn)        | O(n)             |

Explanation

1. Intuition

- There is a crude approach to solve this by generating the ugly numbers,
  till we reach the `n`th ugly number.
- Since the given constraints say that each number's prime factors are limited to `2`, `3`, and `5`,
  we can generate the ugly numbers by multiplying the previous ugly number with `2`, `3`, and `5`.
- Each time we need to find the minimum ugly number then generate the next set of ugly numbers.

- So lets have a min heap to store the ugly numbers.
- To keep track of ugly numbers already generated, we can use a set.
  this prevents us from generating the same ugly number multiple times.
- We can start with the first ugly number `1` and push it to the heap.
- Then we can generate the next ugly numbers by multiplying the current ugly number with `2`, `3`, and `5`.
- Push the new ugly numbers to the heap and keep track of them in the set.
- We can repeat this process till we reach the `n`th ugly number.

2. Implementation

- Initialize a vector of primes `{2, 3, 5}`.
- Initialize a min heap `uglyHeap` of type `long` and a set `visited` of type `long`.
- Push the first ugly number `1` to the heap and insert it into the set.
- Initialize a variable `curr` to store the current ugly number.
- Iterate from `0` to `n`:
  - Get the top element from the heap and store it in `curr`.
  - Pop the top element from the heap.
  - Iterate over the primes:
    - Multiply the current ugly number with the prime and store it in `new_ugly`.
    - If the new ugly number is not present in the set:
      - Push the new ugly number to the heap.
      - Insert the new ugly number into the set.
- Return the current ugly number.

There is also a dynamic programming approach to solve this problem.

DP Solution

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int> primes = {2, 3, 5};  // Initialize the primes array
        vector<int> indices = {0, 0, 0}; // Initialize indices for multiples of 2, 3, 5
        vector<int> uglyArr(1, 1);       // Initialize the ugly number array with 1

        for (int i = 1; i < n; ++i) {
            // Calculate the next possible ugly numbers
            vector<int> next_uglies = {
                uglyArr[indices[0]] * primes[0],
                uglyArr[indices[1]] * primes[1],
                uglyArr[indices[2]] * primes[2]
            };
            int min_value = *min_element(next_uglies.begin(), next_uglies.end()); // Find the smallest value
            uglyArr.push_back(min_value); // Append the smallest value to uglyArr

            // Update indices for primes that generated the current min_value
            for (int j = 0; j < 3; ++j) {
                if (next_uglies[j] == min_value) {
                    ++indices[j];
                }
            }
        }

        return uglyArr[n - 1];
    }
};

Explanation

The above code generates the nth ugly number using dynamic programming.

- We initialize the primes array with `2`, `3`, and `5`.
- We also initialize the indices array with `0`, `0`, and `0`
  to keep track of the multiples of `2`, `3`, and `5`.
- Indicies array will keep track of the next ugly number to be generated for each prime.
- We initialize the ugly number array with `1`.
- We iterate from `1` to `n`:
  - Calculate the next possible ugly numbers by
    multiplying the current ugly numbers with the primes.
  - Find the smallest value from the next possible ugly numbers.
  - Append the smallest value to the ugly number array.
  - Update the indices for the primes that generated the current smallest value.
- Return the `n`th ugly number.