Loading

丑数

丑数的定义

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。

1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

丑数的判断

依次循环除以2、3、5直到不能整除,最后值为1则为丑数。

bool isUgly(int num) {
    if(num <= 0)    return false;
    if(num < 7)     return true;

    while(num % 2 == 0)
        num /= 2;
    while(num % 3 == 0)
        num /= 3;
    while(num % 5 == 0)
        num /= 5;

    return num == 1;
}

找出第N大的丑数

【思路】 动态规划

丑数可以看做是2、3、5的幂的积。

根据幂得到一个丑数容易,问题的难点在于如何使得数据有序。依次递增存储丑数,使用三指针记录当前乘以2、3、5待选的丑数索引,再记录待选中间结果减少计算次数。

int nthUglyNumber(int n) {
    if(n < 7) return n; // 1 2 3 4 5 6
    vector<int> uglyNums(n, 1); // n个丑数 第一个为1
    int index_2 = 0, index_3 = 0, index_5 = 0; // 当前乘2、3、5的丑数列表中值的下标
    int tmp_2 = 2, tmp_3 = 3, tmp_5 = 5;        // 乘2、3、5的待选丑数(减少计算次数)

    for(int i = 1; i < n; ++i){
        uglyNums[i] = min(tmp_2, min(tmp_3, tmp_5)); // 取最小值
        // 更新待选丑数
        // 存在待选值相等的情况
        if(uglyNums[i] == tmp_2){
            tmp_2 = uglyNums[++index_2] * 2;
        }
        if(uglyNums[i] == tmp_3){
            tmp_3 = uglyNums[++index_3] * 3;
        }
        if(uglyNums[i] == tmp_5){
            tmp_5 = uglyNums[++index_5] * 5;
        }
    }

    return uglyNums[n-1];
}

超级丑数

超级丑数是指其所有质因数都是长度为 k 的质数列表 primes 中的正整数。

【思路】 动态规划

// 得到最小待选结果索引集合
vector<int> findMinValueIndex(vector<int>& tmp, const int& k){
    int minValue = tmp[0];  // 待选最小值
    vector<int> minValueIndexs(1, 0);
    for(int i = 1; i < k; ++i){
        if(tmp[i] < minValue){
            minValue = tmp[i]; // 最小值
            minValueIndexs.clear(); // 擦除旧索引
            minValueIndexs.push_back(i); // 新索引
        }else if(tmp[i] == minValue){
            minValueIndexs.push_back(i);
        }
    }

    return minValueIndexs;
}

// 第N个超级丑数
int nthSuperUglyNumber(int n, vector<int>& primes) {
    vector<int> uglyNums(n, 1); // n个丑数  第一个丑数是1
    int k = primes.size();
    vector<int> index(k, 0);    // 索引
    vector<int> tmp(primes);    // 待选数据缓冲

    for(int i = 1; i < n; ++i){
        vector<int> tmpIndex = findMinValueIndex(tmp, k); // 最小待选值索引
        uglyNums[i] = tmp[tmpIndex[0]];
        for(auto& val : tmpIndex){
            ++index[val];
            tmp[val] = uglyNums[index[val]] * primes[val];
        }
    }

    return uglyNums[n-1];
}

丑数 III --- 二分

请你帮忙设计一个程序,用来找出第 n 个丑数。

丑数是可以被 a b c 整除的 正整数。(1不算丑数)

来源:力扣 https://leetcode-cn.com/problems/ugly-number-iii/

class Solution {
/** 【二分法】
给定一个丑数 X :
    1. 该数只能被a整除 (该数一定是a 的整数倍)   {X/a} - {情况4 + 情况5 + 情况7}
    2. 该数只能被b整除 (该数一定是b 的整数倍)   {X/b} - {情况4 + 情况6 + 情况7}
    3. 该数只能被c整除 (该数一定是c 的整数倍)   {X/c} - {情况5 + 情况6 + 情况7}
    4. 该数只能被a和b同时整除 (该数一定是a、b最小公倍数的整数倍)        {X/ab} - {情况7}
    5. 该数只能被a和c同时整除 (该数一定是a、c最小公倍数的整数倍)        {X/ac} - {情况7}
    6. 该数只能被b和c同时整除 (该数一定是b、c最小公倍数的整数倍)        {X/bc} - {情况7}
    7. 该数只能被a和b和c同时整除 (该数一定是a、b、c的最小公倍数的整数倍) {X/abc}
(1, X] 有多少个丑数 :
    {X/a} + {X/b} + {X/c} - {X/ab} - {X/ac} - {X/bc} + {X/abc}
**/
long long ab, ac, bc, abc;  // 最小公倍数

/**
二分查找第 n 个正整数丑数
**/
long long binarySearch(int& n, int& a, int& b, int& c, long long left, long long right){
    if(left >= right)
        return left;
    long long mid = left + ((right - left) >> 1);
    // 计算 (1, mid] 有多少个丑数
    long long num = mid/a + mid/b + mid/c - mid/ab - mid/ac - mid/bc + mid/abc; 
    if(num == n)
        return mid;
    else if(num < n)
        return binarySearch(n, a, b, c, mid+1, right);
    else
        return binarySearch(n, a, b, c, left, mid-1);
}
// 两数最小公倍数
// 乘积 = 最小公倍数 * 最大公约数
long long leastCommonMultiple(int a, int b){    
    long long mult = (long long)a * b;
    // 辗转相除求最大公约数
    while(b > 0){
        int tmp = a % b;
        a = b;
        b = tmp;
    }
    return mult / a;
}

public:
    int nthUglyNumber(int n, int a, int b, int c){
        // 计算最小公倍数
        ab = leastCommonMultiple(a, b);
        ac = leastCommonMultiple(a, c);
        bc = leastCommonMultiple(b, c);
        abc = leastCommonMultiple(ab, c);

        long long left = min(a, min(a, b));
        long long right = left * n;     // 第 n 个丑数不超过第一个丑数的 n 倍
        long ans = binarySearch(n, a, b, c, left, right);

        return ans - min(ans % a, min(ans % b, ans % c));
    }
};
posted @ 2021-01-25 14:48  JakeLin  阅读(214)  评论(0编辑  收藏  举报