剑指Offer:面试题34 丑数

/*
丑数:
我们把只包含因子2、3和5的数称为丑数,求按照从小到大的顺序的第1500个丑数。例如6,8都是丑数,但14不是丑数
,因为它包含因此7.习惯上我们把1当做第一个丑数。

分析:
采用累除法,凡是能除得尽的都是丑数,模拟素数筛选法来做,对模拟的素数筛选法中,2,3,5

书上的方法:
创建数组保存已经找到的丑数,用空间换时间。
创建一个数组,里面的数字时排好序的丑数,每一个丑数都是前面的丑数乘以2,3,5
关键:如何确保里面的丑数是排好序的。
假设数组中已有部分排好序的丑数,把最大的丑数记为M。分析如何生成下一个丑数。
该丑数= 前面某个丑数*(2或3或5),首先把每个已有丑数*2,能得到若干个<=M的结果,而这些必定存在。
我们需要第一个大于M的结果,希望丑数是按从小到大顺序生成。
把得到的第一个乘以2后大于M的结果即为M2,同样乘以3或5,得到M3,M5.
下一个丑数 = min{M2,M3,M5}
对于乘以2而言,存在丑数T2,排在它之前的每个丑数乘以2得到的结果<已有最大丑数,在它之后的每个丑数*2
会太大。我们只需记录这个丑数的位置,同时每次生成新的丑数是,更新T2,对乘3和乘5而言,同样存在T3和T5。


输入:
输入包括一个整数N(1<=N<=1500)。
输出:
可能有多组测试数据,对于每组数据,
输出第N个丑数。
样例输入:
3
样例输出:
3

*/

/*
关键:
1 对于乘以2而言,存在丑数T2,排在它之前的每个丑数乘以2得到的结果<已有最大丑数,在它之后的每个丑数*2
会太大。我们只需记录这个丑数的位置,同时每次生成新的丑数是,更新T2,对乘3和乘5而言,同样存在T3和T5。

2  pMul2 = pMul3 = pMul5 = iUglyNumArr;//初始设定丑数数组中乘以2或3或5后,大于丑数数组中最大值的丑数指针为起始值

3  int iMin = min(*pMul2*2,*pMul3*3,*pMul5*5);//选定丑数
  iUglyNumArr[iNextIndex] = iMin;//设定下一个丑数
  while(*pMul2*2 <= iMin)//更新丑数数组*(2或3或5)大于当前丑数数组中最大值的丑数的位置
  {
   pMul2++;
  }
*/

#include <stdio.h>
const int MAXSIZE = 1501;

int min(int num1,int num2,int num3)
{
 int iMin = num1 < num2 ? num1 : num2;
 iMin = iMin < num3 ? iMin : num3;
 return iMin;
}

int getUglyNum(int n)
{
 if(n < 0 || n > 1500)
 {
  return -1;
 }
 int iUglyNumArr[MAXSIZE];
 iUglyNumArr[0] = 1;
 int iNextIndex = 1;
 int* pMul2,*pMul3,*pMul5;
 pMul2 = pMul3 = pMul5 = iUglyNumArr;//初始设定丑数数组中乘以2或3或5后,大于丑数数组中最大值的丑数指针为起始值
 while(iNextIndex < n)
 {
  int iMin = min(*pMul2*2,*pMul3*3,*pMul5*5);//选定丑数
  iUglyNumArr[iNextIndex] = iMin;//设定下一个丑数
  while(*pMul2*2 <= iMin)//更新丑数数组*(2或3或5)大于当前丑数数组中最大值的丑数的位置
  {
   pMul2++;
  }
  while(*pMul3*3 <= iMin)
  {
   pMul3++;
  }
  while(*pMul5*5 <= iMin)
  {
   pMul5++;
  }
  iNextIndex++;
 }
 return iUglyNumArr[n -1];
}

void process()
{
 int n;
 while(EOF != scanf("%d",&n))
 {
  if(n < 0 || n > 1500)
  {
   continue;
  }
  printf("%d\n",getUglyNum(n));
 }
}

int main(int argc,char* argv[])
{
 process();
 getchar();
 return 0;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值