剑指offer三十三--第n个丑数
前言:本文只提供了解题的思路,代码只有想法二的,但想法二不是最优解。如果你想找一个最好答案,可能本文不合适。如果你想一步一步接近最优答案的话,可以耐心看完。
题目描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
前20个丑数为:1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, 30, 32, 36。
解题思路
想法一
写一个判断是否为丑数的函数,这个函数代码依次将2,3,5除尽。若最后为1则是丑数
从1往后找,找满n个丑数为止
代码如下
bool PanDuan(int val) { if (val == 1) return true; // 把2的因子除尽 while (val % 2 == 0) { val /= 2; } // 把3的因子 while (val % 3 == 0) { val /= 3; } // 5 while (val % 5 == 0) { val /= 5; } if (val == 1) return true; else return false; }
想法二
第i个丑数由前面i-1个数中的某个数 乘以 2 3 5得到,我们找的乘后的数要大于第i-1个丑数,且尽可能的小
那求第i个丑数,将前面的i-1个数依次遍历,遍历一个数时 让其 乘以2 3 5;
结果 要大于第i-1个整数,且尽可能的小。
由前面的东西给推出后面,是一种动态规划的写法,但存在重复计算。
代码见最后;
想法三
方法2存在重复计算 算3要遍历1、2所有 算4 还是要遍历1、2.可以想办法把计算结果保存起来能起来。具体来说 丑数数组A 存放前i-1个丑数数组(没有钱i-1个丑数) 乘以2 3 5后容器B
从B中取最小的数x加入到A中,然后取出的数x乘以2 3 5放入到容器B中
如此反复直到丑数数组A数量达到n
对于容器B 有如下两种方式
容器:一个set
保存方式由两种建一个set 存由已知丑数得到的结果,然后取set最小的值为下一个丑数 乘以2 3 5得到三个数加入set中
缺点是每次加入三个数后都要重新排序,因为顺序不固定
容器:三个队列
保存方式2创建3个队列 乘以2 3 5所得数列,这样避免了重新排序,具体见实例
丑数数组;
(1)丑数数组A1
乘以2队列|2
乘以3队列|3
乘以5队列|5
(2)丑数数组1 2
乘以2 |4
乘以3|3 6
乘以5|5 10
(3)丑数数组1 2 3
乘以2| 4 6
乘以3 |6 9
乘以5|5 10 15
代码C++
想法二
class Solution { public: int GetUglyNumber_Solution(int index) { if (index == 1 || index == 2 || index == 3) return index; vector<int> uglys; uglys.push_back(1); for (int i = 1; i < index; i++) {//依次计算出第i个丑数,i是下标 int min = INT32_MAX; //记录由前面的ugly获取第i个丑数的最小值 for (int j = i - 1; j >= 0; j--) { if (uglys[j] * 2 > uglys[i - 1] && uglys[j] * 2 < min) { min = uglys[j] * 2; } else if (uglys[j] * 3 > uglys[i - 1] && uglys[j] * 3 < min) { min = uglys[j] * 3; } else if(uglys[j] * 5 > uglys[i - 1] && uglys[j] * 5 < min) { min = uglys[j] * 5; } } uglys.push_back(min); } return uglys[index - 1]; } };