264.丑数 II

90 阅读2分钟

题目:
给你一个整数 n ,请你找出并返回第 n 个 丑数 。

丑数 就是只包含质因数 23 和/或 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
}