丑数:最直观的想法是,暴力质因数分解,由于此处质因子只有2、3、5,故与原始质因数分解写法有些许不同。首先编写isUgly函数用于判断数n是否为丑数,由于1、2、3、4、5、6均是丑数,故若index小于等于6,则直接返回index即可,反之记录当前丑数数量num为6,再从7开始遍历,如果当前数是丑数,则对应丑数数量num加一,再判断丑数数量num是否等于给定个数index,如果是则记录结果并返回。(超时)
//判断是否为丑数 与原始的质因数分解不同 bool isUgly(int n) { while(n%2==0) n/=2; while(n%3==0) n/=3; while(n%5==0) n/=5; return n==1; } int GetUglyNumber_Solution(int index) { // 1 2 3 4 5 6均是丑数 if(index<=6) return index; //记录第一个丑数 int num=6; //记录对应结果 int result; for(int i=7;;i++) { //如果是丑数则丑数数量加一 if(isUgly(i)) num++; //如果数量等于待求解的则记录结果 if(num==index) { result=i; break; } } return result; }
优化:上述方法很明显超时,故我们需要找出方法对其进行优化。由于质因子只有2、3、5的为丑数,且所需的n个丑数是按从小到大的顺序进行排列的,假设我们知道前n-1个丑数,那么我们只需将前n-1个丑数每一个分别乘以2、3、5即可得到新的丑数,然后从中检查出与前n-1个丑数不重复的且最小的丑数即可得到第n个丑数。但是这样做法下的时间复杂度仍然很高,所以我们继续想办法优化。根据规律可以知道,第一个丑数为1,将其分别乘以2、3、5,然后取最小的2,由于2是由1乘以2得到的,所以下一个因为乘以2得到的丑数必然是根据2得到的,这时我们就可以根据此单调性规律来设置三指针。具体做法如下:使用res[i]表示第i个丑数,其中第一个丑数是1,分别使用指针p2、p3、p5表示下一个丑数可以根据res[p2]*2、res[p3]*3、res[p5]*5得到,首先根据三指针得到最小的丑数,然后判断该丑数是由哪一个得到的,并将对应的指针后移,当数组长度小于index时一直执行直至相等,最后返回res最后一个元素即可。
int GetUglyNumber_Solution(int index) { // 1 2 3 4 5 6均是丑数 if(index<=6) return index; //用于记录丑数 vector<int> res; //用于记录最小值 int minx; //第一个丑数为1 res.push_back(1); //三指针 分别标记下一个丑数可以根据 res[i]*2 res[j]*3 res[k]*5得到 int p2=0,p3=0,p5=0; //当数组长度小于index一直执行直至相等 while(res.size()<index) { //取最小值 minx=min({res[p2]*2,res[p3]*3,res[p5]*5}); res.push_back(minx); if(minx==res[p2]*2) p2++; if(minx==res[p3]*3) p3++; if(minx==res[p5]*5) p5++; } return res[res.size()-1]; }