• 263. Ugly Number + 264. Ugly Number II + 313. Super Ugly Number


    ▶ 三个与丑数相关的问题

    ▶ 第 263题,判定一个数字是否是丑数,即其素因子是否仅由 2,3,5 构成。

    ● 常规消除判别,4 ms

     1 class Solution
     2 {
     3 public:
     4     bool isUgly(int num)
     5     {
     6         if (num < 1)
     7             return false;
     8         for (; !(num % 2); num /= 2);
     9         for (; !(num % 3); num /= 3);
    10         for (; !(num % 5); num /= 5);
    11         return num == 1;
    12     }
    13 };

    ● 递归方法,6 ms

     1 class Solution
     2 {
     3 public:
     4     bool isUgly(int num)
     5     {
     6         if (num < 1)
     7             return false;
     8         return judge(num);
     9     }
    10     bool judge(int n)
    11     {
    12         if (n < 7)
    13             return true;
    14         if (!(n % 2))
    15             return isUgly(n / 2);
    16         else if (!(n % 3))
    17             return isUgly(n / 3);
    18         else if (!(n % 5))
    19             return isUgly(n / 5);
    20         return false;
    21     }
    22 };

    ▶ 第 264 题,计算第 n 个丑数。第 263 题就是个坑,不能用逐个判断的方法来找,否则超时

    ● 用第 263 题的方法逐个检查,计算 1352 时超时,使用递归法逐个检查也是计算 1352 时超时。

     1 class Solution
     2 {
     3 public:
     4     bool isUgly(int num)
     5     {
     6         if (num < 1)
     7             return false;
     8         for (; !(num % 2); num /= 2);
     9         for (; !(num % 3); num /= 3);
    10         for (; !(num % 5); num /= 5);
    11         return num == 1;
    12     }
    13     int nthUglyNumber(int n)
    14     {
    15         int i, count;
    16         for (i = 1, count = 0; count < n; i++)
    17         {
    18             if (isUgly(i))
    19                 count++;
    20         }
    21         return i - 1;
    22     }
    23 };

    ● 筛法,空间开销爆炸,丑数的增长速度远小于自然数的增长速度

     1 class Solution
     2 {
     3 public:
     4     int nthUglyNumber(int n)
     5     {
     6         if (n < 7)
     7             return n;
     8         vector<bool>table(n * 2, false);
     9         int i, count;
    10         for (i = 1; i < 7; table[i++] = true);// 1 ~ 6 都是丑数,table[0] 闲置
    11         for (; i < n * 2; i++)
    12             table[i] = !(i % 2) && table[i / 2] || !(i % 3) && table[i / 3] || !(i % 5) && table[i / 5];
    13         for (i = 7,count = 6; i < n * 2 && count < n; i++)
    14         {
    15             if (table[i])
    16                 count++;
    17         }
    18         return i - 1;
    19     }
    20 };

    ● 代码,8 ms,正解,每次从已经有的丑数中选出三个来分别乘以 2,乘以 3,乘以 5,谁最小就收录为新的丑数,同时选取的指针向大端移动一格

     1 class Solution
     2 {
     3 public:
     4     int nthUglyNumber(int n)
     5     {
     6         if (n < 7)
     7             return n;        
     8         vector<int> table(n);
     9         int i, t2, t3, t5;
    10         for (i = table[0] = 1, t2 = t3 = t5 = 0; i < n; i++)
    11         {
    12             table[i] = min(table[t2] * 2, min(table[t3] * 3, table[t5] * 5));
    13             if (table[i] == table[t2] * 2)
    14                 t2++;
    15             if (table[i] == table[t3] * 3)
    16                 t3++;
    17             if (table[i] == table[t5] * 5)
    18                 t5++;
    19         }
    20         return table[n - 1];
    21     }
    22 };

    ● 代码,41 ms,使用优先队列,每次将出队的丑数的 2 倍,3 倍,5 倍分别入队,排在后边备选

     1 class Solution
     2 {
     3 public:
     4     int nthUglyNumber(int n)
     5     {
     6         if (n == 1)
     7             return 1;
     8         priority_queue<int, vector<int>, greater<int>> q;
     9         int i, temp;
    10         for (i = 1, q.push(1); i < n; i++)
    11         {            
    12             for (temp = q.top(), q.pop(); !q.empty() && q.top() == temp; q.pop());// 删除队头等值重复                        
    13             if (temp <= 0x7fffffff / 2)
    14                 q.push(temp * 2);
    15             if (temp <= 0x7fffffff / 3)
    16                 q.push(temp * 3);
    17             if (temp <= 0x7fffffff / 5)
    18                 q.push(temp * 5);
    19         }
    20         return q.top();
    21     }
    22 };

    ● 代码,计算不超过一个给定的正整数的丑数的个数,再对所求第 n 个丑数进行二分搜索,计算 1600 时超时。

    ■ 重要的点:

      

     1 class Solution
     2 {
     3 public:
     4     int nums5(int val)// 计算不超过 val 的正整数中形如 5^p 的数字个数
     5     {
     6         int n = 0;
     7         for (n = 0; val >= 5; n++, val /= 5);
     8         return n;
     9     }
    10     int nums35(int val)// 计算不超过 val 的正整数中形如 3^q × 5^p 的数字个数
    11     {
    12         int n;
    13         for (n = 0; val >= 3; n += 1 + nums5(val), val /= 3);
    14             // 第 k 次循环时向 n 增添 3^(k-1) (一个)及形如 3^(k-1) × 5^p 的数字个数(等价于 val / 3^(k-1) 中形如 5^p 的数字个数)
    15         return n;
    16     }   
    17     int nums235(int val)// 计算不超过 val 的正整数中形如 2^r × 3^q × 5^p 的数字个数(丑数个数)
    18     {
    19         int n;
    20         for (n = 0; val >= 2; n += 1 + nums35(val), val /= 2);
    21             // 第 k 次循环时向 n 增添 2^(k-1) (一个)及形如 2^(k-1) × 3^q × 5^p 的数字个数(等价于 val / 2^(k-1) 中形如 3^q × 5^p 的数字个数)
    22         return n;
    23     }    
    24     int nthUglyNumber(int n)//用二分法查找第 n 个丑数,若 ≤ x 的丑数个数是 n,而 ≤ x - 1 的丑数个数是 n - 1,则 x 就是第 n 个丑数
    25     {
    26         if (n < 7)
    27             return n;
    28         n--;                        // 之后的算法中认为 2 是第一个丑数
    29         int valLp, numLp, valRp, numRp, valMp, numMp; 
    30         for (valLp = 1, valRp = 2, numLp = 0, numRp = 1; numRp < n; valLp = valRp, numLp = numRp, valRp = valLp * 2, numRp = nums235(valRp));
    31             // 不断扩大扩大搜索范围,使得在 [ valLp, valRp ] 范围内有 [ numLp, numRp ] 个丑数,valRp = 2 × valLp,numLp ≤ n ≤ numRp
    32         if (numRp == n) // 恰好碰到了上限
    33             return valRp;
    34         for(valMp = (valLp + valRp) / 2;; valMp = (valLp + valRp) / 2)
    35         {
    36             numMp = nums235(valMp);
    37             if (valRp == valMp + 1 && numMp == n - 1 && numRp == n)// val 和 num 在 Mp 和 Rp 之间都有跳跃,Rp 即为所求
    38                 return valRp;
    39             if (valMp == valLp + 1 && numLp == n - 1 && numMp == n)// val 和 num 在 Lp 和 Mp 之间都有跳跃,Mp 即为所求
    40                 return valMp;
    41             if (numMp >= n)     // Mp 偏大
    42                 valRp = valMp, numRp = numMp;
    43             else                // Mp 偏小
    44                 valLp = valMp, numLp = numMp;
    45         }
    46     }
    47 };

    ▶ 第 313 题,扩展丑数,将上述限制条件 “素因子仅由 2,3,5 构成” 改为素因子由一个数组指定,仍然要求第 n 个丑数。

    ● 代码,31 ms,将上述选最小的方法进行扩展

     1 class Solution
     2 {
     3 public:
     4     int nthSuperUglyNumber(int n, vector<int>& primes)
     5     {
     6         const int m = primes.size();
     7         vector<int> table(n), count(m, 0);
     8         int i, j, minValue;
     9         for (i = table[0] = 1; i < n; table[i++] = minValue)
    10         {
    11             for (j = 0, minValue = 0x7fffffff; j < m; j++)
    12             {
    13                 if (minValue > primes[j] * table[count[j]])
    14                     minValue = primes[j] * table[count[j]];
    15             }
    16             for (j = 0; j < m; j++)
    17             {
    18                 if (minValue == primes[j] * table[count[j]])
    19                     count[j]++;
    20             }
    21         }
    22         return table[n - 1];
    23     }
    24 };

    ● 大佬的代码,20 ms,基本相同的算法,优化了循环过程

     1 class Solution
     2 {
     3 public:
     4     int nthSuperUglyNumber(int n, vector<int>& primes)
     5     {
     6         vector<int> ugly(n, 0), ind(primes.size(), 0), val(primes.size(), 1);       
     7         int i, j, next;
     8         for (i = 1, ugly[0] = 1; i < n; ugly[i++] = next)
     9         {            
    10             for (j = 0, next = INT_MAX; j < primes.size(); j++)
    11             {
    12                 if (val[j] == ugly[i - 1])
    13                     val[j] = ugly[ind[j]++] * primes[j];
    14                 next = next < val[j] ? next : val[j];
    15             }
    16         }
    17         return ugly[n - 1];
    18     }
    19 };
  • 相关阅读:
    Java Web学习笔记3
    Java学习笔记11
    diff 比较两个文件的差异
    Java学习笔记10
    appium——如何导出夜神模拟器下载“微信”app的apk
    python之文件操作模块(os和shutil)
    浅谈HTTP和HTTPS
    LUNIX命令集
    ubuntu软件管理工具的使用——dpkg和apt
    CSS学习—day1
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/8399162.html
Copyright © 2020-2023  润新知