【Golang】LeetCode-剑指Offer-面试题49-丑数

题目

我们把只包含因子 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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值