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

news2024/12/26 21:21:38

题目

        我们把只包含质因子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.coloradmin.cn/o/2130590.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单链表的查找与长度计算

注&#xff1a;本文只探讨"带头结点"的情况(查找思路类似循环找到第i-1 个结点的代码) 一.按位查找&#xff1a; 1.代码演示&#xff1a; 版本一&#xff1a; #include<stdio.h> #include<stdlib.h> ​ ​ //定义单链表结点类型 typedef struct LNo…

C语言 | Leetcode C语言题解之第401题二进制手表

题目&#xff1a; 题解&#xff1a; char** readBinaryWatch(int turnedOn, int* returnSize) {char** ans malloc(sizeof(char*) * 12 * 60);*returnSize 0;for (int i 0; i < 1024; i) {int h i >> 6, m i & 63; // 用位运算取出高 4 位和低 6 位if (h &…

针对网上nbcio-boot代码审计的actuator方法的未授权访问漏洞和ScriptEngine的注入漏洞的补救

针对网上下面文章的漏洞补救 奇安信攻防社区-代码审计之nbcio-boot从信息泄露到Getshell 一、未授权分析 在ShiroConfig中放开了actuator方法的未授权访问 org/jeecg/config/shiro/ShiroConfig.java:156 上面问题,先注释掉上面的未授权访问,实际就是代码注释掉 二、、RCE…

秒懂Linux之管道通信

目录 前言 进程间通信目的 管道通信 原理 匿名管道 测试样例 情况与特点 模拟进程池 命名管道 全部代码 前言 两个进程之间可以进行数据的直接传递吗&#xff1f;——不可以&#xff0c;进程必须得具备独立性。 进程间通信目的 数据传输&#xff1a;一个进程需要将…

odoo14 | 报错:Database backup error: Access Denied

这两天抽空想为自己快速做一个简单的管理系统&#xff0c;来信息化管理一下自己家里的一些菜谱、电视剧下载清单等事情&#xff0c;我又不想大动干戈的用Java写管理系统&#xff0c;我就想用已经手生了两年半的odoo快速搭一个系统用用得了&#xff0c;结果还遇上了这么个事 根…

Java设计模式—面向对象设计原则(四) ----->接口隔离原则ISP (完整详解,附有代码+案例)

文章目录 3.4 接口隔离原则(ISP)3.4.1 概述3.4.2 案列 3.4 接口隔离原则(ISP) Interface Segregation Principle&#xff0c;简称ISP 3.4.1 概述 客户端测试类不应该被迫依赖于它不使用的方法&#xff1b;一个类对另一个类的依赖应该建立在最小的接口上。 3.4.2 案列 面看…

PMP--一模--解题--21-30

文章目录 9.资源管理21、 [单选] 项目经理发现一个不可预料的高影响风险已经成为项目的一个因素&#xff0c;团队成员之间的自身利益导致问题得不到解决&#xff0c;项目经理必须快速行动&#xff0c;让团队重新集中精力&#xff0c;以便项目恢复进度&#xff0c;项目经理应该使…

通信工程学习:什么是LCAS链路容量调整机制

LCAS&#xff1a;链路容量调整机制 LCAS&#xff08;Link Capacity Adjustment Scheme&#xff09;链路容量调整机制是一种在ITU-T G.7042中定义的技术&#xff0c;旨在解决传统SDH&#xff08;同步数字体系&#xff09;网络在传输数据业务时带宽分配不灵活的问题。以下是LCAS链…

【 C++ 】C/C++内存管理

前言&#xff1a; &#x1f618;我的主页&#xff1a;OMGmyhair-CSDN博客 目录 一、C/C内存分布 二、C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free malloc&#xff1a; calloc&#xff1a; realloc&#xff1a; free&#xff1a; 三、C内存管理方式…

用Mapmost聚类图分析世界

聚类地图是一种数据可视化工具&#xff0c;能够帮助用户在地图上直观地显示大量地理数据点。当数据点过多时&#xff0c;单独显示每个点会使地图变得混乱&#xff0c;而聚类地图通过将相近的数据点聚集在一起&#xff0c;减少了视觉复杂性&#xff0c;便于分析和理解。聚类地图…

在Linux上安装中创中间件InforSuiteAS(二进制文件安装)

在Linux上安装中创中间件InforSuiteAS&#xff08;二进制文件安装&#xff09; 前言1、环境准备1.1 支持的操作系统1.2 依赖软件 2、安装步骤2.1 下载并解压安装包2.2 执行安装2.3 修改防火墙设置2.4 启动InforSuiteAS2.5 InforSuiteAS常用命令2.6 验证安装 3、常见问题及解决方…

【Petri网导论学习笔记】Petri网导论入门学习(三)

Petri网导论入门学习&#xff08;三&#xff09; Petri 网导论学习笔记&#xff08;三&#xff09;定义 1.4定义 1.5定义 1.6定义 1.7 Petri 网导论学习笔记&#xff08;三&#xff09; 如需学习转载请注明原作者并附本帖链接&#xff01;&#xff01;&#xff01; 如需学习转载…

Axure设计之全屏与退出全屏交互实现

在Axure RP中&#xff0c;设计全屏与退出全屏的交互功能可以极大地提升用户体验&#xff0c;尤其是在展示产品原型或进行演示时。本文将详细介绍如何在Axure RP中通过结合JavaScript代码实现全屏与退出全屏的交互效果。 ​ Axure原型设计web端交互元件库&#xff1a;https://…

网络安全产品认证证书大全(持续更新...)

文章目录 一、引言二、《计算机信息系统安全专用产品销售许可证》2.1 背景2.2 法律法规依据2.3 检测机构2.4 检测依据2.5 认证流程2.6 证书样本 三、《网络关键设备和网络安全专用产品安全认证证书》3.1 背景3.2 法律法规依据3.3 检测机构3.4安全认证和安全检测依据标准3.5 认证…

9月→2024年计算机与信息安全国际会议

【9月→郑州、吉隆坡双会场】 Springer-LNICST &#x1f525;&#x1f525;2024年计算机与信息安全国际会议&#xff08;WCCIS 2024&#xff09; 会议时间&#xff1a;2024年9月20-22日 论文收录&#xff1a;EI&#xff0c;Scopus稳定检索 网络安全&#xff0c;访问控制&am…

了解MySQL 高可用架构:主从备份

为了防止数据库的突然挂机&#xff0c;我们需要对数据库进行高可用架构。主从备份是常见的场景&#xff0c;通常情况下都是“一主一从/(多从)”。正常情况下&#xff0c;都是主机进行工作&#xff0c;从机进行备份主机数据&#xff0c;如果主机某天突然意外宕机&#xff0c;从机…

Android 13 固定systemUI的状态栏为黑底白字,不能被系统应用或者三方应用修改

目录 一.背景 二.思路 三.代码流程 1.colos.xml自定义颜色 2.设置状态栏的背景颜色 3.对View进行操作 ①.对Clock(状态栏左侧的数字时钟)进行操作 ②.对电池(BatteryMeterView)进行操作 4.锁屏状态栏 5.patch汇总 一.背景 客户需求将状态栏固定成黑底白字,并且不能让系…

红外小目标检测:基于深度学习

目录 ​编辑 1.红外成像技术的优势 2.红外小目标检测的基本原理 常用方法 1. 背景抑制法 2. 基于滤波的方法 3. 基于模型的方法 4. 基于深度学习的方法 5. 多传感器融合方法 3.代码实战 案例背景 数据准备 模型选择 代码实现 讲解 4.应用场景 5.未来发展趋势 …

NISP 一级 | 4.2 操作系统的安全威胁

关注这个证书的其他相关笔记&#xff1a;NISP 一级 —— 考证笔记合集-CSDN博客 0x01&#xff1a;漏洞和漏洞扫描 对计算机系统安全威胁最大的就是系统本身的漏洞&#xff0c;只有存在漏洞&#xff0c;黑客才有机会入侵我们的计算机系统。具统计证明&#xff0c;99% 的黑客攻击…

基于spring实现博客项目的删除和更新(五)

8. 实现用户退出 前端直接清除掉token即可. 实现客⼾端代码 <注销>链接已经提前添加了onclick事件 &#xff0c;在common.js中完善logout⽅法 function logout(){localStorage.removeItem("user_token");location.href "blog_login.html"; } 点击…