Python面试宝典第48题:找丑数

题目

        我们把只包含质因子2、3和5的数称作丑数(Ugly Number)。比如:6、8都是丑数,但14不是,因为它包含质因子7。习惯上,我们把1当做是第一个丑数。求按从小到大的顺序的第n个丑数。

        示例 1:

输入:5
输出:5

        示例 2:

输入:7
输出:8

暴力法

        本题最直观的解法是使用暴力法,即从1开始逐个检查每个自然数是否为丑数,直到找到第n个丑数为止。使用暴力法求解本题的主要步骤如下。

        1、定义一个函数,用于判断一个数是否为丑数。

        2、从1开始遍历每一个数,对每个数调用该函数。

        3、计数器记录已经找到的丑数的数量,当计数器达到n时,返回当前检查的数作为第n个丑数。

        根据上面的算法步骤,我们可以得出下面的示例代码。

def is_ugly_number(num):
    if num <= 0:
        return False
    for factor in [2, 3, 5]:
        while num % factor == 0:
            num //= factor
    return num == 1

def find_ugly_numbers_by_brute_force(n):
    count = 0
    num = 1
    while True:
        if is_ugly_number(num):
            count += 1
            if count == n:
                return num
        num += 1

print(find_ugly_numbers_by_brute_force(5))
print(find_ugly_numbers_by_brute_force(7))

优先队列法

        优先队列法利用了最小堆的数据结构来保持未处理的丑数候选,其基本思想为:初始时,我们将1加入到优先队列中,因为1是最小的丑数;之后,每次从队列中取出最小的元素,并将其与2、3、5相乘后再次加入队列中。为了避免重复加入相同的丑数,我们需要记录之前加入队列的最大值,并确保不加入小于等于该最大值的数。使用优先队列法求解本题的主要步骤如下。

        1、初始化堆和已知丑数。

        (1)创建一个最小堆heap,并向其中添加第一个丑数1。

        (2)创建一个集合seen,用于存储已经发现的丑数,以防止重复计算。

        (3)设置变量last_ugly为 1,这代表最后加入的丑数。

        (4)设置计数器count为 1,用来跟踪已经找到的丑数的数量。

        2、循环直到找到第n个丑数,并执行以下操作。

        (1)每次循环从堆中弹出最小的丑数current,并使用三个质因数(2、3、5)分别乘以current来生成新的丑数。

        (2)对于每个新生成的丑数new_ugly,检查它是否已经被计算过,即是否存在于seen集合中。

        (3)如果new_ugly还未被计算过,则将其添加到seen集合中,并将其推入堆heap中。

        3、更新最后加入的丑数。

        (1)如果弹出的丑数current不等于last_ugly,这意味着我们找到了一个新的丑数。

        (2)更新last_ugly为current,并将count加一。

        (3)当count达到n时,循环结束,此时last_ugly即为第n个丑数。

        4、返回last_ugly作为第n个丑数。

        根据上面的算法步骤,我们可以得出下面的示例代码。

import heapq

def find_ugly_numbers_by_priority_queue(n):
    # 初始化最小堆
    heap = [1]
    # 记录最后一个加入堆的丑数
    last_ugly = 1
    # 已找到的丑数数量
    count = 1
    # 使用集合来去重
    seen = set([1])
    
    while count < n:
        # 弹出当前最小的丑数
        current = heapq.heappop(heap)
        
        # 生成新的丑数并加入堆中
        for factor in [2, 3, 5]:
            new_ugly = current * factor
            # 如果新生成的丑数还没有被加入过,则加入堆中
            if new_ugly not in seen:
                seen.add(new_ugly)
                heapq.heappush(heap, new_ugly)
        
        # 只有在当前丑数不等于最后一个加入的丑数时,才进行下一步操作
        if current != last_ugly:
            last_ugly = current
            count += 1
    
    # 最后一个加入的丑数就是第n个丑数
    return last_ugly

print(find_ugly_numbers_by_priority_queue(5))
print(find_ugly_numbers_by_priority_queue(7))

动态规划法

        使用动态规划法的关键是:定义状态转移方程。假设dp[i]表示第i个丑数,则本题的状态转移方程为:dp[i] = min(2 * dp[j], 3 * dp[k], 5 * dp[l])。其中,j、k、l分别表示最近乘以2、3、5得到dp[i]的索引。边界条件为dp[1] = 1,因为1是最小的丑数。使用动态规划法求解本题的主要步骤如下。

        1、初始化数组dp,长度为n + 1,其中dp[1] = 1。

        2、定义三个指针j、k、l,初始值均为1。

        3、对于每个i(从2到n),进行以下操作。

        (1)计算dp[i]的值,它是2 * dp[j]、3 * dp[k]、5 * dp[l]中的最小值。

        (2)如果dp[i]等于2 * dp[j],则j加1。

        (3)如果dp[i]等于3 * dp[k],则k加1。

        (4)如果dp[i]等于5 * dp[l],则l加1。

        4、当i达到n时,dp[n]就是第n个丑数。

        根据上面的算法步骤,我们可以得出下面的示例代码。

def find_ugly_numbers_by_dp(n):
    if n == 1:
        return 1
    
    # 初始化dp数组
    dp = [0] * (n + 1)
    dp[1] = 1
    j, k, l = 1, 1, 1
    
    for i in range(2, n + 1):
        # 计算下一个丑数
        dp[i] = min(2 * dp[j], 3 * dp[k], 5 * dp[l])
        
        # 更新指针
        if dp[i] == 2 * dp[j]:
            j += 1
        if dp[i] == 3 * dp[k]:
            k += 1
        if dp[i] == 5 * dp[l]:
            l += 1
    
    return dp[n]

print(find_ugly_numbers_by_dp(5))
print(find_ugly_numbers_by_dp(7))

总结

        暴力法的实现较为简单,但效率低下,尤其是在n较大的时候。最坏情况下,需要检查所有的数直到找到第n个丑数。假设第n个丑数是u(n),则暴力法的时间复杂度为O(u(n))。其空间复杂度为O(1),只需要常数级别的额外空间。

        使用优先队列法时,插入和删除操作的时间复杂度为O(logu(n)),且需要做n次这样的操作,故总的时间复杂度为O(n*logu(n))。堆的大小最多为u(n),故总的空间复杂度为O(u(n))。与暴力法相比,优先队列法的效率更高,但可能带来较多的空间消耗。

        使用动态规划法时,每个数只被访问一次,故时间复杂度为O(n)。其空间复杂度也为O(n),因为仅需要一个长度为n的数组来存储结果。动态规划法在时间和空间上都较为平衡,故实际应用中,通常倾向于使用动态规划法来解决这类问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/875196.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

另类动态规划

前言&#xff1a;一开始我根本想不到这个题目是一个动态规划的题目&#xff0c;而且我一开始的初始状态还写错了 我还忘记了写算法题的基本步骤&#xff0c;先看数据范围&#xff0c;再考虑能不能用动态规划写 题目地址 #include <bits/stdc.h> using namespace std; #de…

3C电子胶黏剂在手机制造方面有哪些关键的应用

3C电子胶黏剂在手机制造方面有哪些关键的应用 3C电子胶黏剂在手机制造中扮演着至关重要的角色&#xff0c;其应用广泛且细致&#xff0c;覆盖了手机内部组件的多个层面&#xff0c;确保了设备的可靠性和性能。以下是电子胶在手机制造中的关键应用&#xff1a; 手机主板用胶&…

浏览器百科:网页存储篇-IndexedDB介绍(十)

1.引言 在现代网页开发中&#xff0c;数据存储需求日益增多和复杂&#xff0c;传统的客户端存储技术如localStorage和sessionStorage已难以满足大型数据的存储和管理需求。为了解决这一问题&#xff0c;HTML5 引入了 IndexedDB&#xff0c;在本篇《浏览器百科&#xff1a;网页…

网络学习-eNSP配置路由器

#PC1网关&#xff1a;192.168.1.254 #PC3网关&#xff1a;192.168.3.254 #PC4网关&#xff1a;192.168.4.254# 注&#xff1a;路由器接口必须配置不同网段IP地址 <Huawei>system-view Enter system view, return user view with CtrlZ. #给路由器两个接口配置IP地址 [Hua…

IBM中国研发中心撤出:挑战与机遇并存

IBM中国研发中心撤出&#xff1a;挑战与机遇并存 引言 近日&#xff0c;IBM宣布撤出在中国的两大研发中心的消息&#xff0c;引起了广泛关注。这一举动不仅对IBM自身的全球布局产生了影响&#xff0c;也在一定程度上反映了跨国公司在中国市场策略的调整。本文将探讨这一事件背…

keras和tensorflow可用的一组版本

目录 keras版本&#xff1a;3.5.0tensorflow&#xff1a;2.17.0之前的错误导包现在的正确导包 keras版本&#xff1a;3.5.0 tensorflow&#xff1a;2.17.0 之前的错误导包 其实也不是说错误&#xff0c;就是因为文件位置不对&#xff0c;所以VSCode总是有黄色波浪线&#xff0…

只出现一次的数II

只出现一次的数&#xff1a;力扣&#xff08;LeetCode&#xff09;-----只出现一次的数 题目描述 给你一个整数数组 nums &#xff0c;除某个元素仅出现 一次 外&#xff0c;其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 示例 1&#xff1a; 输入&am…

C# WinForm:禁用Panel容器滚动条自动移动位置的功能

1.在WinForm项目中新建一个类&#xff1a; 2.类里面的内容&#xff0c;重写Panel的这个方法 3.编译后这个控件就出现在工具箱了 4.然后用这个新Panel控件就好了 5.完事大吉。

Datasheet SHT20芯片的数据手册

Datasheet SHT20芯片的数据手册 I2C读取湿度传感器返回的16位数据。SCL SDA 14位有效&#xff0c;我以为是将后二位删除&#xff0c;实际上看完手册才知道是后二位值无用&#xff0c;不是删除&#xff0c;而是清0&#xff0c;实际上还是16为&#xff0c;知识后二位是0还是1&…

深度学习——基础知识

深度学习的重点在于优化&#xff0c;其中很重要的步骤在于如何调参&#xff0c;会涉及到一些微积分等数学知识。不同于以往接触到的数值运算&#xff0c;深度&#xff08;机器&#xff09;学习都是关于张量Tensor&#xff08;向量&#xff09;的计算&#xff0c;Python中最常用…

leetcode21. 合并两个有序链表

思路&#xff1a; 用一个新链表来表示合并后的有序链表&#xff0c; 每次比较两个链表&#xff0c;将较小的那个结点存储至新链表中 # Definition for singly-linked list. # class ListNode(object): # def __init__(self, val0, nextNone): # self.val val # …

机器学习(西瓜书)第 9 章 聚类

9.1 聚类任务和距离计算 在”无监督学习“中&#xff0c;训练样本的标记信息是未知的&#xff0c;目标是通过对无标记训练样本的学习来揭示数据的内在性质及规律&#xff0c;为进一步的数据分析提供基础.此类学习任务中研究最多、应用最广的是“聚类”(clustering). 聚类试图…

动手学深度学习(pytorch)学习记录30-含并行连接的网络(GoogLeNet)[学习记录]

目录 GoogLeNetInception块GoogLeNet模型训练模型 GoogLeNet GoogLeNet&#xff0c;也称为Inception v1&#xff0c;是由Google团队在2014年提出的深度学习模型&#xff0c;它在当年的ImageNet竞赛中取得了显著的成绩。GoogLeNet的设计引入了多个创新点&#xff0c;包括Incept…

【C++】string类的基本使用

一、string类的由来 在C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列 的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP的思想&#xff0c;而且底层空间需要用户 自己管…

大数据之Flink(三)

9.3、转换算子 9.3.1、基本转换算子 9.3.1.1、映射map 一一映射 package transform;import bean.WaterSensor; import org.apache.flink.streaming.api.datastream.DataStreamSource; import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator; impor…

分类预测|基于改进的灰狼IGWO优化支持向量机SVM的数据分类预测matlab程序 改进策略:Cat混沌与高斯变异

分类预测|基于改进的灰狼IGWO优化支持向量机SVM的数据分类预测matlab程序 改进策略&#xff1a;Cat混沌与高斯变异 文章目录 一、基本原理原理流程1. **定义目标函数**2. **初始化GWO**3. **评估适应度**4. **更新狼的位置**5. **更新狼的等级**6. **重复迭代**7. **选择最佳解…

春招审核新策略:Spring Boot系统实现

3系统分析 3.1可行性分析 通过对本大学生入学审核系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本大学生入学审核系统采用Spring Boot框架&#xff0c;JA…

综合案例-数据可视化-柱状图

一、基础柱状图 我们绘制一个关于三种水果销售额的柱状图&#xff0c;X轴数据为三种水果的名称&#xff0c;用列表[苹果,香蕉,橘子]添加进去&#xff0c;Y轴数据为三种水果的销售额&#xff0c;用列表[50,70,60]添加进去。 步骤&#xff1a; 导包构建柱状图对象添加X轴数据生…

Android 12系统源码_窗口管理(八)WindowConfiguration的作用

前言 在Android系统中WindowConfiguration这个类用于管理与窗口相关的设置&#xff0c;该类存储了当前窗口的显示区域、屏幕的旋转方向、窗口模式等参数&#xff0c;应用程序通过该类提供的信息可以更好的适配不同的屏幕布局和窗口环境&#xff0c;以提高用户体验。 一、类定…

喜报 | 知从科技荣获 “AutoSec 安全之星 - 优秀汽车软件供应链安全方案奖”

近日&#xff0c;「AutoSec 2024第八届中国汽车网络安全周暨第五届智能汽车数据安全展」在上海盛大举行。本届大会由谈思实验室和谈思汽车主办、上海市车联网协会联合主办&#xff0c;以汽车“网络数据安全、软件安全、功能安全”为主题&#xff0c;设置了“31X”模式&#xff…