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 };
原文地址:https://www.cnblogs.com/cuancuancuanhao/p/8399162.html