题目
我们把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:
1 是丑数。
n 不超过1690。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/chou-shu-lcof
解题思路(三指针)
我们知道,丑数的排列肯定是1,2,3,4,5,6,8,10…
- 特点:任意一个丑数都是由小于它的一个丑数 *2,*3 或者 *5 得到的
那么如何得到所有丑数呢?
-
现在假设有3个数组,分别是:
- A:{1 * 2,2 * 2,3 * 2,4 * 2,5 * 2,6 * 2,8 * 2,10 * 2…}
- B:{1 * 3,2 * 3,3 * 3,4 * 3,5 * 3,6 * 3,8 * 3,10 * 3…}
- C:{1 * 5,2 * 5,3 * 5,4 * 5,5 * 5,6 * 5,8 * 5,10 * 5…}
-
那么所有丑数的排列,必定就是上面 A 、B 、C 这3个数组的合并结果然后去重得到的
-
那么这不就转换成了
- 三个有序数组的无重复元素合并的问题了吗?
- 而这三个数组就刚好是 {1,2,3,4,5,6,8,10…} 乘以 2, 3, 5 得到的。
合并有序数组
合并有序数组的一个比较好的方法,就是
-
每个数组都对应一个指针,然后比较这些指针所指的数中哪个最小,就将这个数放到结果数组中,然后该指针向后挪一位。
-
回到本题,要求丑数ugly数组中的第 n 项
-
而目前只知道 ugly [0] = 1,所以此时三个有序数组分别就只有一个元素:
- A :{1 * 2…}
- B :{1 * 3…}
- C :{1 * 5…}
-
假设三个数组的指针分别是 a, b, c,此时均是指向第一个元素,
-
然后比较 A[a],B[b] 和 C[c] ,得到的最小的数A[a],就是ugly[1],
-
此时ugly就变成{1,2}了,对应的ABC数组就分别变成了:
- A :{1 * 2 , 2 * 2…}
- B :{1 * 3 , 2 * 3…}
- C :{1 * 5 , 2 * 5…}
-
此时根据合并有序数组的原理
- A 数组指针 a 就指向了下一个元素,即 ’ 2 * 2 ’ , 而 b 和 c 依然分别指向 B[b] 和 C[c]
- 然后进行下一轮合并,就是 A[1] 和 B[0] 和 C[0] 比较,最小值作为ugly[2]
- 如此循环 n 次,就可以得到 ugly [n] 了。
注意
- 到 A B C 三个数组实际上就是 ugly[ ] * 2,ugly[ ] * 3 和 ugly[ ] * 5 的结果,
- 所以每次只需要比较 A[a] = ugly[a] * 2,B[b] = ugly[b] * 3 和 C[c] = ugly[c] * 5 的大小即可。
- 取出最小值,就把其对应的数组指针往后移动一个位置
去重
- 如果多个数组中的元素都是最小,那么这多个数组的指针都要往后移动一个位置。
代码
func nthUglyNumber(n int) int {
if n == 0{
return 0
}
ugly := make([]int,n)
ugly[0] = 1
a,b,c := 0,0,0
for i := 1;i < n;i++{
A,B,C := ugly[a]*2 , ugly[b]*3 , ugly[c]*5
tmp := min(A,min(B,C))
if tmp == A{
a++
}
if tmp == B{
b++
}
if tmp == C{
c++
}
ugly[i] = tmp
}
return ugly[n-1]
}
func min(a,b int)int{
if a > b{
return b
}
return a
}