一、题目
丑数是指不能被2,3,5以外的其他素数整除的数。把丑数从小到大排列起来,结果如
下:
1,2,3,4,5,6,8,9,10,12,15,…
求第1500个丑数。
答案:859963392
二、优先队列说明
优先队列本质上是堆排序,虽然说是按照优先级来排序,但是对于单纯的数字集合来说,就是简单的按照从小到大或者从大到小的顺序排序。
priority_queue <int>//默认从小到大,当然int可以换成其他的数据类型,下面的也是类似的
priority_queue <int,vector<int>,less<int> > q;//按照从大到小排序
priority_queue <int,vector<int>,greater<int> > q;//按照从小到大排序
还有一些队列的基本操作
q.size();//返回q的元素个数
q.empty();//判断q是否为空,空则返回1,否则返回0
q.push(k);//在q的末尾插入k
q.pop();//删除q的第一个元素
q.top();//返回q的第一个元素
vector是队列内部用于承载底层数据结构堆的容器,less是对第一个参数的比较类。
那这个优先级具体在项目上面有什么作用呢?比如你是开蔬菜店的,就可以用优先队列优先出售价格低的啊,新鲜的蔬菜啊等等。
三、思路分析
本题解法很多,但是第一情况下,大家是不是会想到一个数一个数的循环遍历,并用一个变量(sum)记录,如果是能被2,3,5整除的素数,看sum是否等于第1500,到了就输出那个数。
**注意:**这种方法固然没有问题,但是如果数据量过大,让你求第好几亿个呢,得用多少空间,多少时间呢?因此我们在写算法的时候需要考虑题目要求的时间空间限制。这题可以用优先队列解决。
解题优化思路: 最小的丑数是1,其他所有数x,2x,3x,4x,5x都是丑数,如果用一个优先队列保存所有已生成的丑数,每次把最小的丑数取出来,然后生成三个新的丑数。但有一点要注意,在生成过程中难免会产生几个数的公倍数,所以得用set数组的不允许有重复元素的特性,用count()方法判断是否已经生成过。
四、代码
#include<iostream>
#include<vector>
#include<queue>
#include<set>
typedef long long ll;
const int a[3] = { 2, 3, 5 };
using namespace std;
int main() {
priority_queue<ll, vector<ll>, greater<ll> > pq; //创造一个优先队列,默认按照从小到大排序
set<ll> s;
pq.push(1); //先向集合和队列里面存入第一个丑数1,有了第一个数就是不断生成新的丑数。
s.insert(1); //设置一个set集合,保证之后的元素没有重复的
for (int i = 1; ; i++) {
ll x = pq.top(); //获取队列的第一个元素
pq.pop(); //踢掉第一个元素
if (i == 1500) {
cout << "第1500个数是: " << x << endl;
break;
}
for (int j = 0; j < 3; j++) { //用这个数分别乘以2,3,5,得到三个新的丑数,和之前的数作比较,如果没有出现过,就假如集合
ll x1 = x * a[j];
if (!s.count(x1)) {
s.insert(x1);
pq.push(x1);
}
}
}
return 0;
}