丑数

题目:
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

思路:
看到网上有两种思路,一种是最小堆的思想,一种是三指针,类似于动态规划的思想,下面针对三指针动态规划详细说明一下。
三指针动态规划的思想其实就是类似于合并两个有序数组的思想。
记丑数数组为ugly;那么ugly 的排列一定是{1,2,3,4,5,6,8……}。
根据丑数的定义,任意一个丑数都是由小于它的某一个丑数×因子(2或3或5)得到的。假设当前的丑数是ugly[i],如何得到有序数组的下一个丑数元素呢?
现在假设有3个数组,分别是
A:{1×2,2×2,3×2,4×2,……}
B:{1×3,2×3,3×3,4×3,……}
C:{1×5,2×5,3×5,4×5,……}
观察上面数组的特点,都是由较小的丑数乘以不同的丑数因子得到的丑数数组,那么所有的整数排列就是这三个数组的不断的动态去重、合并排序得到的。

假设三个数组的指针分别是p2,p3,p5. 然后比较A[p2],B[p2],C[p5],得到最小的数ugly[1] = 2,那么这时候ugly数组就是{1,2}了。p2指针应向右挪一个。为了去重,如果多个元素都是最小,那么这多个指针都要往后移动一个。
代码如下:

class Solution {
public:
    int nthUglyNumber(int n) {
        //丑数是除了从7开始的,质数 以及他们的倍数。
     //三指针法,类似于合并两个有序数组,这里是合并
 
        if(n==1) return 1;
        int p2 = 0,p3=0,p5=0;
        vector<int> ugly(n,0);
        ugly[0] =1;
        for(int i=1;i<n;i++){
            int num2 = 2*ugly[p2], num3 = 3*ugly[p3], num5 = 5*ugly[p5];
            ugly[i] = min(min(num2,num3),num5);
            if(ugly[i]==num2) p2++;
            if(ugly[i]==num3)  p3++;
            if(ugly[i]==num5)  p5++;
        }
        return ugly[n-1];

    }
};

第二种方法:最小堆
利用无序数组unordered_set去重。
初始时,堆为空,将1压入堆中,假如x是丑数,那么2x,3x,5x都是丑数 ,所以将他们压入堆中,执行n次就能找到第n大的丑数,只是这样浪费的空间会更多一点。

class Solution {
public:
    int nthUglyNumber(int n) {
    	vector<int> factors={2,3,5};
    	unordered_set<long> seen;
    	priority_queue<long,vector<long>,greater<long>> pq;
    	seen.insert(1L);
    	pq.push(1L);
    	int ugly = 0;
    	for(int i=0;i<n;i++){
    		long cur = pq.top();
			ugly = (int)cur;
			pq.pop();
			for(auto factor: factors){
				int next = cur*factor;
				if(!seen.count(next)){
					seen.insert(next);
					pq.push(next);
				}
			}
		}
		return ugly;

    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值