求第n个丑数--coding

1、求第n个丑数 (质因数只有2,3,5)(leetcode 264)

2、设计算法,找到质因数只有3,5或7的第k个数。

以上两个问题相似

阿里笔试题请设计一个算法,在满足质因数仅为3,5,7或其组合的数中,找出第K大的数。比如K=1,2,3时,分别应返回3,5,7。要求算法时间复杂度最优。

我们可以用3个队列来维护这些数。第1个队列负责乘以3,第2个队列负责乘以5, 第3个队列负责乘以7。算法描述如下:
1. 初始化结果res=1和队列q3,q5,q7
2. 分别往q3,q5,q7插入1*3,1*5,1*7
3. 求出三个队列的队头元素中最小的那个x,更新结果res=x
4. 如果x在:
    q3中,那么从q3中移除x,并向q3,q5,q7插入3*x,5*x,7*x
    q5中,那么从q5中移除x,并向q5,q7插入5*x,7*x
    q7中,那么从q7中移除x,并向q7插入7*x
5. 重复步骤3-5,直到找到第k个满足条件的数
注意,当x出现在q5中,我们没往q3中插入3*x,那是因为这个数在q5中已经插入过了。

#include <iostream>
#include <queue>
using namespace std;
 
int mini(int a, int b){
    return a < b ? a : b;
}
int mini(int a, int b, int c){
    return mini(mini(a, b), c);
}
int get_num(int k){
    if(k <= 0) return 0;
    int res = 1, cnt = 1;
    queue<int> q3, q5, q7;
    q3.push(3); q5.push(5); q7.push(7);
    for(; cnt<k; ++cnt){
        int v3 = q3.front();
        int v5 = q5.front();
        int v7 = q7.front();
        res = mini(v3, v5, v7);
        if(res == v7){
            q7.pop();
        }
        else{
            if(res == v5){
                q5.pop();
            }
            else{
                if(res == v3){
                    q3.pop();
                    q3.push(3*res);
                }
            }
            q5.push(5*res);
        }
        q7.push(7*res);
    }
    return res;
}
int main(){
    for(int i=1; i<20; ++i)
        cout<<get_num(i)<<endl;
    return 0;
}

该方法的具体分析:https://blog.csdn.net/wangfengfan1/article/details/48006071

该问题的另一种思路:https://blog.csdn.net/qingyuanluofeng/article/details/54124669

求第n个丑数 (质因数只有2,3,5)

优化思路:

并不用每次都尝试所有组合,我们需要过滤掉不可能的组合
已知2,3,5与已有丑数相乘时,数值增长速度是不一样的
我们每次从三组新丑数中,挑选大于已知末尾丑数的最小值,来作为新的丑数
乘3或者乘5的新丑数,如果某次未被选中放入数轴,那么肯定有某一刻会被放进去
所以使用三个游标来确定已被放入数轴的新丑数最小值,每次仅需要对比这三个游标与2,3,5乘积即可


首先想一想,丑数是由只含2,3,5的因子组成的,那一个丑数乘上2,3,5仍会得到一个丑数,所以生成新丑数可以以旧丑数为基础,然后乘以2,3,5 得到新的丑数;如何使生成的丑数保持递增的顺序,每次循环会生成一个新丑数,与原来两个旧丑数做比较,取最小的加入数组中。
首先初始化dp[0]=1,第一次循环,dp[0]*2,dp[0]*3,dp[0]*5,取最小2,更新index2 = index2 + 1,第二次循环,dp[1]*2,dp[0]*3,dp[0]*5,取最小3,更新index3 = index3 + 1,第三次循环,dp[1]*2,dp[1]*3,dp[0]*5,取最小4,更新index5 = index5 + 1,如此下去,得出结果

int FindUgly(int n) 
{     
    int* ugly = new int[n];     
    ugly[0] = 1;     
    int index2 = 0;     
    int index3 = 0;     
    int index5 = 0;     
    int index = 1;  // 维护丑数数组
    while (index < n)     
    {     
        int val = Min(ugly[index2]*2, ugly[index3]*3, ugly[index5]*5); //竞争产生下一个丑数 (最小)    
        if (val == ugly[index2]*2) //将产生这个丑数的index*向后挪一位;    
            ++index2;     
        if (val == ugly[index3]*3)   //这里不能用elseif,因为可能有两个最小值,这时都要挪动;  
            ++index3;     
        if (val == ugly[index5]*5)     
            ++index5; 
        index = index + 1;    
        ugly[index] = val;     
    }  
}

7、计算n以内质因数只有3、5、7的数个数

int main() {
  long x;
  cin >> x;
  int sum = -1;
  for(long i = 1; i<= x; i *= 7){
      for(long j = i;  j <= x; j *= 5) {
          for(long k = j; k <= x; k *= 3){
              ++sum;
          }
      }    
  }
  cout << sum << endl;
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值