[230522 剑指49] 丑数

文章介绍了如何使用动态规划求解第n个丑数的问题,丑数是指只包含质因子2、3和5的数。通过维护三个下标a、b、c,分别对应于当前序列乘以2、3、5的结果,每次选取最小值并更新下标,最终找到第n个丑数。同时提到了C语言中的变长数组特性及其限制。
摘要由CSDN通过智能技术生成

[230522 剑指49] 丑数

一 题目

剑指 Offer 49. 丑数

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

示例:

输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

说明:

  1. 1 是丑数。
  2. n 不超过1690。

二 整体思路

**(确定解法)从后往前看:**因为丑数只包含质因子2、3、5,所以每一个丑数都是由在它之前的丑数序列中的某一个数乘 2、乘 3 或乘 5 得到的,于是我们可以想到动态规划的解法。

**(确定递推公式)从前往后看:**已有的丑数序列中的每一个丑数都应该乘以2、乘以3、乘以5,将其值记录到丑数序列中,这样才不会漏掉某一个丑数。但是因为前面的数乘以 5 的值可能会大于后面的数乘以 2 或 3 的值,所以我们不能单纯地按顺序记录丑数序列中每一个数乘2、乘3、乘5的结果。

所以我们可以使用下标的方式:

  • 使用下标 a 表示下标 a 之前的每一个丑数乘 2 的结果都已被加入到丑数序列中
  • 使用下标 b 表示下标 b 之前的每一个丑数乘 3 的结果都已被加入到丑数序列中
  • 使用下标 c 表示下标 c 之前的每一个丑数乘 5 的结果都已被加入到丑数序列中

在构造丑数序列时,选取下标为 a 的丑数乘 2 的结果、下标为 b 的丑数乘 3 的结果、下标为 c 的丑数乘 5 的结果中的最小值,先加入到丑数序列中,然后对应的下标递增。

三 关键点/重点/难点

难点在确定递推公式(上文说得比较清楚了)。

另外一个易错点在,丑数 x 乘 2 的结果可能与丑数 y 乘 3 的结果相等,并且作为最小值被记录到丑数序列中,此时 a 和 b 都应该递增,所以要注意下标递增的写法:if 语句时并列的。

四 代码分析

class Solution {
public:
    int nthUglyNumber(int n) {
        int dp[n];
        //dp[i]: 第i+1个丑数
        dp[0] = 1;
        int a = 0, b = 0, c = 0;
        for(int i = 1; i < n; ++i) {
            dp[i] = min(min(dp[a] * 2, dp[b] * 3), dp[c] * 5);
            if(dp[i] == dp[a] * 2) ++a;
            if(dp[i] == dp[b] * 3) ++b;
            if(dp[i] == dp[c] * 5) ++c;
        }
        return dp[n - 1];
    }
};

(五)一题多解

没有

(六) 知识扩展

C 中的变长数组:

在 C99 以后,支持变长数组,也就是说,数组的大小是可以在运行时确定的,它的内存依旧是分配在栈区的。

//可以正常运行
int size;
printf("enter the array size: ");
scanf("%d",&size);
int a[size];

但变长数组的一个明显缺点是,如果 size 很大,则数组的分配可能会失败,并且无法检查分配是否失败,将发生运行时错误(例如,segmentfault)。因此,如果数组大小太大时,需要避免使用变长数组。除此之外,变长数组的作用域仅仅是块范围,如果需要更大的作用域,建议使用动态数组。

留疑:当变长数组为多大时会发生栈溢出呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值