本科四年,研究生已经第二年,虽说一直是计算机专业,编码能力越来越弱,面试了几个实习生职位,由于没有准备就去了,不出意外的全悲剧了。下定决心开始努力加强,把理论知识一点一点付诸于实践。从今天开始,每天写一篇代码题,在此立帖为证。。。
以前经常下载CSDN的东西,现在来这里记录吧,也算做出点贡献。每篇题目必定亲自验证,算法思想会借鉴网上思路,但有自己思考、敲代码。完全是自己记录学习过程,涉及到他人版权,还请见谅。
题目1:把只包含质因子2、3和5的数称作丑数(Ugly Number),例如:2,3,4,5,6,8,9,10,12,15,等,习惯上我们把1当做是第一个丑数。写一个高效算法,返回第n个丑数。
首先,想到的应该是暴力求解。从1开始循环,判断每个数是不是丑数,当count累加到n时,停止循环。
void findugly(int n)
{
int i=2;
int d;
int count = 1;
while(count<n)
{
d=i;
while(d%2==0)
d=d/2;
while(d%3==0)
d=d/3;
while(d%5==0)
d=d/5;
if(d==1)
count++;
i++;
}
printf("第%d个丑数是:%d\n",n,(i-1));
}
void main()
{
clock_t start, finish;
double duration;
start = clock();
int d ;
printf("请输入数字:");
scanf("%d",&d);
findugly(d);
finish = clock();
duration=finish - start;
printf("%f seconds \n", duration/1000);
}
运行结果,请输入数字:1500
第1500个丑数是:859963392
26.222000 seconds
虽然在四核机器上运行,此时程序执行时间还是很长的,原因是循环次数太多,寻找到第1500个丑数时,i已经非常大。如何减少复杂度呢?我们不按照i从一开始依次循环,每个丑数必定只有2 3 5因子,那么它们必定是这几个数相互乘法而得到的,按照此思路,以一个数组ugly[n]来存储丑数,每次从三个相乘后的结果中选择一个最小的存入数组,那么同样可以得到所求结果,并且复杂度降低了。(以下代码摘自网络)
int min(int a, int b, int c)
{
int temp = (a < b ? a : b);
return (temp < c ? temp : c);
}
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;
ugly[index++] = val;
}
int result = ugly[n-1];
delete[] ugly;
return result;
}
void main()
{
clock_t start, finish;
double duration;
start = clock();
int d ;
printf("请输入数字:");
scanf("%d",&d);
printf("%d\n",findugly(d));
finish = clock();
duration=finish - start;
printf("%f seconds\n ", duration/1000);
}
运行结果,请输入数字:1500
859963392
1.596000 seconds
可见,采取这种方法后,时间非常快,实际上,按照动态规划思想,要求出第n个丑数,必须要遍历前n-1个丑数,那么这已经是最优的了,或与最优相差常数时间。