剑指offer(三十三)——丑数
题目描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
题解
理解题意很重要,咋一看会感觉丑数很复杂,但实际上你把它列出来就会发现,丑数定义是比较简单的。1、2、3、4、5、6、8、9、10、12、15、18、20、25……仔细琢磨就会发现
- 2=1*2
- 3=1*3
- 5=1*5
- 4=2*2
- 6=2*3
- 10=2*5
- 6=3*2
- 9=3*3
- 15=3*5
- ……
可以看出来丑数都是由丑数相乘得来的。在知道了定义之后我们能做什么?这里有一位大佬的解释就很清楚。
链接:https://www.nowcoder.com/questionTerminal/6aa9e04fc3794f68acf8778237ba065b?f=discussion
来源:牛客网维护三个队列:
(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
这样看思路是不是比较明朗了?但是3个队列就显得比较麻烦,所以我们用3个指针也可以达到相同的效果。
- p1、p2、p3三个指针用来取代队列的作用,由于默认1是丑数数组的第一位,所以先让指针指向丑数数组list[0],分别乘上2、3、5,结果中最小的便是下一位丑数,这里是2。
- 将2存入list中,并移动指针p1,然后重复乘上2、3、5,结果最小即是下一位丑数。
- 这样便可以求出题目所要求的数了。
public int GetUglyNumber_Solution(int index) {
if (index < 7) {
return index;
}
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
int i, p1 = 0, p2 = 0, p3 = 0;
for(i = 1; i < index; i++) {
int mulTwo = list.get(p1) * 2;
int mulThr = list.get(p2) * 3;
int mulFiv = list.get(p3) * 5;
int minNum = Math.min(mulTwo, Math.min(mulThr, mulFiv));
list.add(minNum);
if (minNum == mulTwo) {
p1++;
}
if (minNum == mulThr) {
p2++;
}
if (minNum == mulFiv) {
p3++;
}
}
return list.get(index - 1);
}