题目:
给你一个整数 n ,请你找出并返回第 n 个 丑数 。
丑数 就是只包含质因数 2、3 和/或 5 的正整数。
算法:
方法一:动态规划
一个实际的例子:
p2 p3 p5 dp
1 1 1 2
2 1 1 3
2 2 1 4
3 2 2 5
4 3 2 6
5 3 2 8
5 4 2 9
6 4 3 10
dp保存第i位丑数,i>1 时, dp中所有的丑数都是由min(dp[2]...dp[i - 1]) 乘以2,3,5的最小值决定。
用三个指针i2, i3, i5 指向没有加入dp的丑数中,i2 * 2最小的丑数, i33最小的丑数,i55最小的丑数,在这三个丑数里找到最小值,就是下一个丑数。
找到之后将对应的index加一,指向下一个没用过的最小一个丑数。
func nthUglyNumber(n int) int {
dp := make([]int, n + 1)
i2, i3, i5 := 1, 1, 1
dp[1] = 1
for i := 2; i <= n; i ++ {
num2 := dp[i2] * 2
num3 := dp[i3] * 3
num5 := dp[i5] * 5
dp[i] = min(num2, num3, num5)
// 这里不能if else,
// dp[i2] * 2 = 3 * 2和dp[i3] * 3 = 2 * 3
// i2和i3都要+1
if dp[i] == num2{
i2 ++
}
if dp[i] == num3{
i3 ++
}
if dp[i] == num5{
i5 ++
}
}
return dp[n]
}
func min(a, b, c int) int {
min := a
if b < min {
min = b
}
if c < min {
min = c
}
return min
}
方法二:小根堆
将1放入小根堆,每次取出最小的丑数,乘以2,3,5,得到三个丑数,去重放入堆中。第n次从小根堆中拿出的元素就是第n个丑数。
import "container/heap"
func nthUglyNumber(n int) int {
ans := 1
exist := map[int]struct{}{
1:struct{}{},
}
h := &IntHeap{1}
heap.Init(h)
for i := 1; i <= n; i ++ {
x := heap.Pop(h).(int)
if n == i {
ans = x
break
}
n2 := x * 2
n3 := x * 3
n5 := x * 5
if _, ok := exist[n2]; !ok {
heap.Push(h, n2)
exist[n2] = struct{}{}
}
if _, ok := exist[n3]; !ok {
heap.Push(h, n3)
exist[n3] = struct{}{}
}
if _, ok := exist[n5]; !ok {
heap.Push(h, n5)
exist[n5] = struct{}{}
}
}
return ans
}
type IntHeap []int
func (h IntHeap) Len() int {return len(h)}
func (h IntHeap) Swap(i, j int) {h[i], h[j] = h[j], h[i]}
func (h IntHeap) Less(i, j int) bool {return h[i] < h[j]}
func (h *IntHeap) Push(x interface{}) {
*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n - 1]
*h = old[:n - 1]
return x
}