这是剑指第一次卡死我的题……记录一下

首先看题目:

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

一开始的思路:

一开始,我就直接蛮力,就按照它的描述,从2到根号n一个个试,看看是不是它的因子,是的话是不是质数,是质数的话是不是2或者3或者5什么的。

最后做出来,前面几个数字都是可以通过测试的,但提交就一直说我超时通过率为0……然后本地测了下超时的用例1500——emmm确实是,算了半天都没出来。

然后百度,百度百科上对丑数的判别:

首先除2,直到不能整除为止,然后除5到不能整除为止,然后除3直到不能整除为止。最终判断剩余的数字是否为1,如果是1则为丑数,否则不是丑数。
 
用这个思路又写了个,从2开始往上+1,然后每个都这样判断是不是丑数,好的还是超时。(其实这个和我的蛮力一样,只是我的蛮力比较恶心。)
 
 
最后没办法看讨论,大多用的都是一个思路:
所有的丑数都是前面的那个丑数乘以2或者3或者5而来的。所以我们可以维护三个队列和一个正式丑数序列:
链接:https://www.nowcoder.com/questionTerminal/6aa9e04fc3794f68acf8778237ba065b
来源:牛客网

(1)丑数数组: 1
乘以2的队列:2
乘以3的队列:3
乘以5的队列:5
选择三个队列头最小的数2加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(2)丑数数组:1,2
乘以2的队列:4

乘以3的队列:3,6

乘以5的队列:5,10

选择三个队列头最小的数3加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;

(3)丑数数组:1,2,3

乘以2的队列:4,6

乘以3的队列:6,9

乘以5的队列:5,10,15

选择三个队列头里最小的数4加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;

(4)丑数数组:1,2,3,4

乘以2的队列:6,8

乘以3的队列:6,9,12

乘以5的队列:5,10,15,20

选择三个队列头里最小的数5加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;

(5)丑数数组:1,2,3,4,5

乘以2的队列:6,8,10,

乘以3的队列:6,9,12,15

乘以5的队列:10,15,20,25

选择三个队列头里最小的数6加入丑数数组,但我们发现,有两个队列头都为6,所以我们弹出两个队列头,同时将12,18,30放入三个队列;

然后我按照这个思路,自己实现了次,也遇到了很多问题……

我是真正地维护了三个队列,然后每次把最新的丑数乘以2,3,5然后分别加到三个队列中,然后取出最小的数字处理,从哪个队列中取出就哪个队列出队。队列直接用PriorityQueue,可以直接获得最小值。

遇到的问题是:

  1. 我这个直接按照上面的思路做的话,到后面数字很大会溢出……然后溢出的话,比如一个很大的丑数*5溢出了,就变成一个负数然后以后的最小值就是这个负数了——我的解决方法是:重写一个Compator,然后传入PriorityQueue中去。

  2. 这还不够,因为到了后面,可能出现一个队列中的所有数字都小于0的情况,所以在拿出数字后比较出最小的这一步也要考虑到负数的情况。

看下我的这个思路的实现的代码,已经通过测试:

import java.util.*;

public class Solution {
public int GetUglyNumber_Solution(int index) {
if(index <= 0)return 0;
if(index == 1)return 1; Queue<Integer> q2 = new PriorityQueue<Integer>(new NewComparator());
Queue<Integer> q3 = new PriorityQueue<Integer>(new NewComparator());
Queue<Integer> q5 = new PriorityQueue<Integer>(new NewComparator()); int uglyNum = 1, count = 1, minTemp;
while(true) {
q2.add(uglyNum * 2);
q3.add(uglyNum * 3);
q5.add(uglyNum * 5); //下面的操作这么麻烦是因为会有几个队列有着同样的最小值的情况,这时候都要让它出队
minTemp = getMinOfThree(q2.peek(), q3.peek(), q5.peek(), count); if(q2.peek() == minTemp)q2.poll();
if(q3.peek() == minTemp)q3.poll();
if(q5.peek() == minTemp)q5.poll(); uglyNum = minTemp;
count++;
if(count == index)return uglyNum;
}
} // 找三个数字中没有溢出的最小值
private int getMinOfThree(int a, int b, int c, int count) { if (a < 0 || b < 0 || c < 0) {// 存在溢出情况,有元素小于0
int[] temp = new int[3];
temp[0] = a;
temp[1] = b;
temp[2] = c;
Arrays.sort(temp); for (int x : temp) {
if (x < 0)
continue;
return x;
}
}
return a < b ? (a < c ? a : c) : (b < c ? b : c);
} //因为之前的那个,如果有数字溢出了,就会变成负数,负数肯定最小然后就会被poll出来,最后结果就会有错误
private class NewComparator implements Comparator<Integer> { @Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
if(o1 < 0)return 1;
if(o1 < o2)return -1;
else if(o1 == o2)return 0;
else return 1;
} } }

但其实我们可以不用维护三个队列:

链接:https://www.nowcoder.com/questionTerminal/6aa9e04fc3794f68acf8778237ba065b
来源:牛客网

们没有必要维护三个队列,只需要记录三个指针显示到达哪一步;“|”表示指针,arr表示丑数数组;

(1)1

|2

|3

|5

目前指针指向0,0,0,队列头arr[0] * 2 = 2,  arr[0] * 3 = 3,  arr[0] * 5 = 5

(2)1 2

2 |4

|3 6

|5 10

目前指针指向1,0,0,队列头arr[1] * 2 = 4,  arr[0] * 3 = 3, arr[0] * 5 = 5

(3)1 2 3

2| 4 6

3 |6 9

|5 10 15

目前指针指向1,1,0,队列头arr[1] * 2 = 4,  arr[1] * 3 = 6, arr[0] * 5 = 5
 
 
上一个简化后不用维护三个队列的Java的实现代码:
public int GetUglyNumber_Solution2(int index) {
if (index <= 0)
return 0;
ArrayList<Integer> list = new ArrayList<Integer>();
// add进第一个丑数1
list.add(1);
// 三个下标用于记录丑数的位置
int i2 = 0, i3 = 0, i5 = 0;
while (list.size() < index) {
// 三个数都是可能的丑数,取最小的放进丑数数组里面
int n2 = list.get(i2) * 2;
int n3 = list.get(i3) * 3;
int n5 = list.get(i5) * 5;
int min = Math.min(n2, Math.min(n3, n5));
list.add(min);
if (min == n2)
i2++;
if (min == n3)
i3++;
if (min == n5)
i5++;
}
return list.get(list.size() - 1);
}

可以看见,就是三个指针在动。而且这里似乎不会出现溢出有负数的情况,因为我那里每次是用最新的丑数去乘2,3,5;而这里是用之前的丑数,就i2,i3,i5指针所在的那个丑数来乘,所以好很多。

剑指Offer丑数问题的更多相关文章

  1. 剑指Offer——丑数

    剑指Offer--丑数 前言     参照<剑指Offer>,通过洞悉其思想并消化吸收,改为java实现,供自己以后巩固. package cn.edu.ujn.offersword; i ...

  2. 剑指offer 丑数

    思路:可以发现,每个丑数都是由以前的丑数得到.当前丑数一定是之前丑数能够得到的最小丑数. AC代码 class Solution { public: int GetUglyNumber_Solutio ...

  3. 用js刷剑指offer(丑数)

    题目描述 把只包含质因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含质因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. 思路 ...

  4. 剑指offer——丑数(c++)

    题目描述只包含质因子2.3和5的数称作丑数(UglyNumber).例如6.8都是丑数,但14不是,因为它包含质因子7,习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. 思路:1.逐个 ...

  5. 剑指offer--33.丑数

    本来用数组做标志位,但是测试数据有第1500个,859963392,惹不起哦 ------------------------------------------------------------- ...

  6. 剑指Offer-32.丑数(C++/Java)

    题目: 把只包含质因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含质因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. 分析: ...

  7. 剑指Offer32 丑数

    /************************************************************************* > File Name: 32_UglyNu ...

  8. 干货 | 剑指offer系列文章汇总

    下面是名企面试中经常会出现的面试题目,大家可以戳相应的题目查看题目细节,其答案会在紧接着的后一篇中出现  剑指offer系列  始 剑指offer—灯管问题(1)  剑指offer—10人电梯(2)  ...

  9. 【剑指offer】丑数

    把只包含因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. leetcode上也 ...

随机推荐

  1. mesos的zookeeper变更

    采用rpm方式安装你了mesos,碰到zookeeper(采用了cloudera的zookeeper)的IP地址变化了,肿么办? 在master机器中: /etc/mesos/zk进行编辑修改zk路径 ...

  2. Poj 3356 ACGT(LCS 或 带备忘的递归)

    题意:把一个字符串通过增.删.改三种操作变成另外一个字符串,求最少的操作数. 分析: 可以用LCS求出最大公共子序列,再把两个串中更长的那一串中不是公共子序列的部分删除. 分析可知两个字符串的距离肯定 ...

  3. PAT1106(BFS)

    PAT 1106 思路 BFS用在tree上,这一个题里主要关注的是用vector去保存每一个节点所连接的子节点,当BFS 时,一旦发现该节点下面没有子节点,这一层一定是最短的路径,然后用当前的层数去 ...

  4. TModJS:目录

    ylbtech-TModJS:目录 1.返回顶部 1. https://github.com/aui/tmodjs 2. https://www.npmjs.com/package/tmodjs 3. ...

  5. modbus读输入状态与读线圈状态的区别?

    01 读线圈状态 描述 读从机离散量输出口的 ON/OFF 状态,不支持广播.附录B列出由不同控制器型号支持最大的参数清单. 查询 查询信息规定了要读的起始线圈和线圈量,线圈的起始地址为零,1-16个 ...

  6. 数据库高手(DBA专家 ,SSIS,replacation ,tourble shooting)

    http://www.cnblogs.com/qanholas/category/266780.html 随笔分类 - mssql SQL Server 2008 Datetime Cast 成 Da ...

  7. JavaScript语言精粹知识点总结

    1.NaN是一个数值,它表示一个不能产生正常结果的运算结果.NaN不等于任何值,包括它自己. 2.Infinity表示所有大于1.79769313486231570e+308的值,所以Infinity ...

  8. [poj2135]Farm Tour(最小费用流)

    解题关键:最小费用流 代码一:bellma-ford $O(FVE)$  bellman-ford求最短路,并在最短路上增广,速度较慢 #include<cstdio> #include& ...

  9. [51nod1181]质数中的质数(素数筛法)

    解题关键: 注意下标 #include<bits/stdc++.h> #define maxn 10000002 using namespace std; typedef long lon ...

  10. display与position之间的关系

    以防自己忘记写的 网上找的 positon 与 display 的相互关系 元素分为内联元素和区块元素两类(当然也有其它的),在内联元素中有个非常重要的常识,即内两元素是不可以设置区块元素所具有的样式 ...