【LeetCode】A collection of ugly number questions

263. Ugly numbers

Insert image description here
Insert image description here
Insert image description here

Ideas

  • First of all, ugly numbers must be positive integers , so n<1you can directly return false for;
  • For n >= 1, if n is divisible by 2/3/5, it means they are ugly numbers.

code

class Solution {
    
    
public:
    bool isUgly(int n) {
    
    
        // ugly只能是正整数
        if(n < 1) return false;
        vector<int> factors = {
    
    2, 3, 5};
        for(int i=0; i<=2; ++i){
    
    
            while(n % factors[i] == 0){
    
    
                n /= factors[i];
            }
        }
        return n == 1;
    }
};

264. Number of Ox II

Insert image description here
Insert image description here
Insert image description here

Method 1: Minimum heap

Ideas

  • To get the nth ugly number, you can use a min-heap implementation.
  • The initialization heap is empty, and the smallest ugly number 1 is added first. Each time the top element x of the heap is taken out, x is the smallest ugly number in the heap. 2x, 3x, and 5x must also be ugly numbers, so they are also added to the minimum heap.
  • However, the above approach will cause duplicate elements . In order to avoid this situation, use a hash set to remove duplicates and avoid adding the same elements to the heap multiple times.
  • When duplicate elements are excluded, the nth element taken out from the minimum heap is the nth ugly number.

code

class Solution {
    
    
public:
    int nthUglyNumber(int n) {
    
    
        vector<int> factor = {
    
    2, 3, 5};
        priority_queue<long, vector<long>, greater<long>> heap;
        unordered_set<long> s;
        // 先把丑数 1 加入
        s.insert(1L);
        heap.push(1L);
        long cur;
        for(int i=0; i<n; ++i){
    
    
            cur = heap.top();
            heap.pop();
            for(int f : factor){
    
    
                // 依次计算 2x 3x 5x
                long temp = cur * f;
                // s中没有temp 将其存入
                if(!s.count(temp)){
    
    
                    s.insert(temp);
                    heap.push(temp);
                }
            }
        }
        return (int)cur;
    }
};

Method 2: Dynamic programming (three-pointer method)

Ideas

  • Method 1 uses the minimum heap, which will store more ugly numbers in advance, and the process of maintaining the minimum heap also leads to high time complexity. Dynamic programming methods can be used for optimization.

  • Define the array dp, where dp[i] represents the i-th ugly number, then dp[n] is the answer to this question. Among them, dp[1] = 1.

  • We can calculate the remaining ugly numbers through three pointers p 2 , p 3 , and p 5 . The meaning of p i is the position of the clownest number that is eligible to be multiplied by i. The qualification here refers to: If an ugly number nums[pi ] can be multiplied by i to get the next ugly number, then this ugly number nums[p i ] will never be qualified to be multiplied by i. We put p i ++, just let nums[p i ] point to the next ugly number.

  • for example:

    At the beginning, the only ugly numbers are {1}. 1 can be multiplied by 2, 3, and 5, and the smallest 1×2=2 is added to the ugly number sequence.

    Now there are {1, 2} in the ugly numbers. In the previous step, 1 has been multiplied by 2, so there is no need to compare 1×2 in the future, so it is considered that 1 has lost the qualification to be multiplied by 2.

    Now 1 is qualified to be multiplied by 3 and 5, and 2 is qualified to be multiplied by 2, 3 and 5, but 2×3 and 2×5 are definitely larger than 1×3 and 1×5, so there is no need to compare. So we only need to compare 1×3, 1×5, 2×2 .

    By analogy, each time we compare the ugliest numbers that are qualified to be multiplied with 2, 3, and 5, and select the smallest one as the next ugly number. Assume that the ugly number selected is the same as i (i=2, 3, 5), so it loses the qualification to multiply with i. Just put the corresponding p i ++ and let p i point to the next ugly number.

code

class Solution {
    
    
public:
    int nthUglyNumber(int n) {
    
    
        vector<int> dp(n+1);
        dp[1] = 1;
        int p2 = 1, p3 = 1, p5 = 1;
        for(int i=2; i<=n; ++i){
    
    
            int num2 = 2 * dp[p2];
            int num3 = 3 * dp[p3];
            int num5 = 5 * dp[p5];
            dp[i] = min(min(num2, num3), num5);
            // 确定dp[i]是由哪个数字生成的
            if(dp[i] == num2)   p2++;
            if(dp[i] == num3)   p3++;
            if(dp[i] == num5)   p5++;
        }
        return dp[n];
    }
};

1201. Ugly Number III

Insert image description here

Insert image description here
Insert image description here

Method: binary search + inclusion-exclusion principle

Ideas

  • The first thing to note is that the definition of "ugly number" in this question is different from the previous two questions. The ugly number x cannot be directly enumerated here (three-pointer method), because x is too large and will cause a timeout.

  • This question is an upgraded version of 878. The Nth magic number . Change the two numbers to three numbers to make it more difficult. Compared with question 878, this question only needs to modify the function for finding the number of ugly numbers less than or equal to x, and the binary search part is exactly the same. It is recommended to review question 878 first. The thought map for this question is attached below.

    Insert image description here

  • The sets consisting of multiples of a, multiples of b, and multiples of c that are less than or equal to ∪B∪C∣ can be obtained from the inclusion-exclusion principle :

    ∣A∪B∪C∣=∣A∣+∣B∣+∣C∣−∣A∩B∣−∣A∩C∣−∣B∩C∣+∣A∩B∩C∣

  • Therefore, the number of ugly numbers less than or equal to x is: x/a + x/b + x/c - x/lcm_a_b - x/lcm_b_c - x/lcm_a_c + x/lcm_a_b_c. You can use the least common multiple function std::lcm.

  • Next, through binary search , the boundary is continuously reduced until the number corresponding to a certain position contains exactly n ugly number factors.

code

class Solution {
    
    
public:
    using ll = long long;
    ll nthUglyNumber(ll n, ll a, ll b, ll c) {
    
    
        ll lcm_a_b = std::lcm(a, b), lcm_a_c = std::lcm(a, c), lcm_b_c = std::lcm(b, c);
        ll lcm_a_b_c= std::lcm(lcm_a_b, c);
        // 最小的丑数必然是a、b、c的最小值
        ll left = min(min(a, b), c);
        ll right = n * left;
        while(left + 1 < right){
    
    
            ll mid = (left + right) / 2;
            if(mid/a + mid/b + mid/c - mid/lcm_a_b - mid/lcm_a_c - mid/lcm_b_c + mid/lcm_a_b_c < n){
    
    
                left = mid;
            }
            else right = mid;
        }
        return right;
    }
};

313. Super ugly number

Insert image description here
Insert image description here
Insert image description here

Method: "Multiple-way merge"

Ideas

  • This question is actually 264. 丑数 IIan advancement of . The former uses three pointers, corresponding to 2, 3, and 5 respectively. However, the length of the primes array in this question is not fixed, so a pointer array is used to correspond to each value of primes.

  • The first ugly number must be 1, and "ugly numbers generated in the future" are all based on "existing ugly numbers" (use "existing ugly numbers" multiplied by "given prime factors" primes[i] ). The specific process is shown in the figure.

    Insert image description here

    • Obviously, we need to take the smallest value each time, then move the pointer backward (pointing to the next ugly number), and repeat this process until the nth ugly number is found.
    • In addition, since each of our pointer movements and the construction of dp are monotonically increasing , deduplication can be achieved by comparing with dp[i-1] without referencing the Set structure.

code

class Solution {
    
    
public:
    long nthSuperUglyNumber(int n, vector<int>& primes) {
    
    
        int len = primes.size();
        // 指针数组
        vector<long> ptr(len, 1);
        vector<long> dp(n+1, 0);
        dp[1] = 1;
        for(int i=2;i<=n;++i){
    
    
            int flag = 0;
            dp[i] = primes[0] * dp[ptr[0]];
            for(int j=0; j<len; ++j){
    
    
                long cur = primes[j] * dp[ptr[j]];
                if(cur < dp[i]){
    
    
                    flag = j;
                    dp[i]= cur;
                }     
            }
            ptr[flag]++;
            // 如果当前值和上一个丑数一样,那么跳过该丑数
            if(dp[i] == dp[i-1]) i--;
        
        }
        return dp[n];
    }
};

Summarize

  • The above are all related questions about ugly numbers. The definition of ugly numbers is not fixed, so you need to carefully understand the meaning of the questions before starting to calculate.
  • For 263. 丑数, it can be solved using simple mathematical ideas ;
  • For , the minimum heap and dynamic programming (that is, the three-pointer264. 丑数II method) are used , but the minimum heap method is more time-consuming, and the second method is more recommended;
  • For 1201. 丑数III, different from the general definition of ugly numbers, it is an upgraded version of 878. the Nth magic number , which uses binary search and inclusion-exclusion principles ;
  • For 313.超级丑数, is an upgraded version of , it can be solved by 264. 丑数IIextending the three-pointer method to multiple pointers , which can also be understood as a multi-way merge method .

References

  1. Ugly Numbers II official solution
  2. How to understand the three-pointer method
  3. 313. Super ugly number: [Mitsuha Miyamizu] One question with two solutions: "Priority Queue" & "Multiple Merge"

Guess you like

Origin blog.csdn.net/weixin_43894455/article/details/132129527