49. 丑数

devtools/2024/10/7 23:23:29/

comments: true
difficulty: 中等
edit_url: https://github.com/doocs/leetcode/edit/main/lcof/%E9%9D%A2%E8%AF%95%E9%A2%9849.%20%E4%B8%91%E6%95%B0/README.md

面试题 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。

注意:本题与主站 264 题相同:https://leetcode.cn/problems/ugly-number-ii/

解法

质因子是一个正整数的质数因子。即,质因子是将该整数分解为质数的过程中的组成部分。例如,24的质因子是2和3,因为24可以分解为
2的3次方 * 3的1次方

方法一:优先队列(最小堆)

初始时,将第一个丑数 1 1 1 加入堆。每次取出堆顶元素 x x x,由于 2 x 2x 2x, 3 x 3x 3x, 5 x 5x 5x 也是丑数,因此将它们加入堆中。为了避免重复元素,可以用哈希表 v i s vis vis 去重。

时间复杂度 O ( n × log ⁡ n ) O(n \times \log n) O(n×logn),空间复杂度 O ( n ) O(n) O(n)

Python3
import heapq
class Solution:def nthUglyNumber(self, n: int) -> int:h = [1]vis = {1}ans = 1 #每次heappop出的元素就是 按顺序得到答案for _ in range(n):# 通过模拟堆push元素 和 pop元素,就可以清楚过程ans = heapq.heappop(h)for v in [2, 3, 5]:nxt = ans * vif nxt not in vis:vis.add(nxt)heapq.heappush(h, nxt)return ans
Java
class Solution {public int nthUglyNumber(int n) {Set<Long> vis = new HashSet<>();PriorityQueue<Long> q = new PriorityQueue<>();int[] f = new int[] {2, 3, 5};q.offer(1L);vis.add(1L);long ans = 0;while (n-- > 0) {ans = q.poll();for (int v : f) {long next = ans * v;if (vis.add(next)) {q.offer(next);}}}return (int) ans;}
}
C++
class Solution {
public:int nthUglyNumber(int n) {priority_queue<long, vector<long>, greater<long>> q;q.push(1l);unordered_set<long> vis{{1l}};long ans = 1;vector<int> f = {2, 3, 5};while (n--) {ans = q.top();q.pop();for (int& v : f) {long nxt = ans * v;if (!vis.count(nxt)) {vis.insert(nxt);q.push(nxt);}}}return (int) ans;}
};
Go
func nthUglyNumber(n int) int {h := IntHeap([]int{1})heap.Init(&h)ans := 1vis := map[int]bool{1: true}for n > 0 {ans = heap.Pop(&h).(int)for _, v := range []int{2, 3, 5} {nxt := ans * vif !vis[nxt] {vis[nxt] = trueheap.Push(&h, nxt)}}n--}return ans
}type IntHeap []intfunc (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x any) {*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() any {old := *hn := len(old)x := old[n-1]*h = old[0 : n-1]return x
}
Rust
impl Solution {pub fn nth_ugly_number(n: i32) -> i32 {let n = n as usize;let mut dp = vec![1; n];let mut p2 = 0;let mut p3 = 0;let mut p5 = 0;for i in 1..n {let n2 = dp[p2] * 2;let n3 = dp[p3] * 3;let n5 = dp[p5] * 5;dp[i] = n2.min(n3.min(n5));if dp[i] == n2 {p2 += 1;}if dp[i] == n3 {p3 += 1;}if dp[i] == n5 {p5 += 1;}}dp[n - 1]}
}
JavaScript
/*** @param {number} n* @return {number}*/
var nthUglyNumber = function (n) {let dp = [1];let p2 = 0,p3 = 0,p5 = 0;for (let i = 1; i < n; ++i) {const next2 = dp[p2] * 2,next3 = dp[p3] * 3,next5 = dp[p5] * 5;dp[i] = Math.min(next2, Math.min(next3, next5));if (dp[i] == next2) ++p2;if (dp[i] == next3) ++p3;if (dp[i] == next5) ++p5;dp.push(dp[i]);}return dp[n - 1];
};
C#
public class Solution {public int NthUglyNumber(int n) {int[] dp = new int[n];dp[0] = 1;int p2 = 0, p3 = 0, p5 = 0;for (int i = 1; i < n; ++i) {int next2 = dp[p2] * 2, next3 = dp[p3] * 3, next5 = dp[p5] * 5;dp[i] = Math.Min(next2, Math.Min(next3, next5));if (dp[i] == next2) {++p2;}if (dp[i] == next3) {++p3;}if (dp[i] == next5) {++p5;}}return dp[n - 1];}
}
Swift
class Solution {func nthUglyNumber(_ n: Int) -> Int {var vis = Set<Int64>()var pq = PriorityQueue<Int64>()let factors: [Int64] = [2, 3, 5]pq.push(1)vis.insert(1)var ans: Int64 = 0for _ in 0..<n {ans = pq.pop()!for factor in factors {let next = ans * factorif vis.insert(next).inserted {pq.push(next)}}}return Int(ans)}
}struct PriorityQueue<T: Comparable> {private var heap: [T] = []var isEmpty: Bool {return heap.isEmpty}mutating func push(_ element: T) {heap.append(element)heapifyUp(from: heap.count - 1)}mutating func pop() -> T? {guard !heap.isEmpty else {return nil}if heap.count == 1 {return heap.removeLast()}let value = heap[0]heap[0] = heap.removeLast()heapifyDown(from: 0)return value}private mutating func heapifyUp(from index: Int) {var index = indexlet element = heap[index]while index > 0 {let parentIndex = (index - 1) / 2if element >= heap[parentIndex] {break}heap[index] = heap[parentIndex]index = parentIndex}heap[index] = element}private mutating func heapifyDown(from index: Int) {var index = indexlet element = heap[index]let count = heap.countwhile index < count / 2 {var childIndex = index * 2 + 1if childIndex + 1 < count && heap[childIndex + 1] < heap[childIndex] {childIndex += 1}if element <= heap[childIndex] {break}heap[index] = heap[childIndex]index = childIndex}heap[index] = element}
}

方法二:动态规划

我们定义数组 d p dp dp,其中 d p [ i − 1 ] dp[i-1] dp[i1] 表示第 i i i 个丑数,那么第 n n n 个丑数就是 d p [ n − 1 ] dp[n - 1] dp[n1]。最小的丑数是 1 1 1,所以 d p [ 0 ] = 1 dp[0]=1 dp[0]=1

定义 3 3 3 个指针 p 2 p_2 p2, p 3 p_3 p3 p 5 p_5 p5,表示下一个丑数是当前指针指向的丑数乘以对应的质因数。初始时,三个指针的值都指向 0 0 0

i i i [ 1 , 2.. n − 1 ] [1,2..n-1] [1,2..n1] 范围内,我们更新 d p [ i ] = min ⁡ ( d p [ p 2 ] × 2 , d p [ p 3 ] × 3 , d p [ p 5 ] × 5 ) dp[i]=\min(dp[p_2] \times 2, dp[p_3] \times 3, dp[p_5] \times 5) dp[i]=min(dp[p2]×2,dp[p3]×3,dp[p5]×5),然后分别比较 d p [ i ] dp[i] dp[i] d p [ p 2 ] × 2 dp[p_2] \times 2 dp[p2]×2, d p [ p 3 ] × 3 dp[p_3] \times 3 dp[p3]×3, d p [ p 5 ] × 5 dp[p_5] \times 5 dp[p5]×5 是否相等,若是,则对应的指针加 1 1 1

最后返回 d p [ n − 1 ] dp[n - 1] dp[n1] 即可。

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

Python3
class Solution:def nthUglyNumber(self, n: int) -> int:dp = [1] * np2 = p3 = p5 = 0for i in range(1, n):next2, next3, next5 = dp[p2] * 2, dp[p3] * 3, dp[p5] * 5dp[i] = min(next2, next3, next5)# 通过对应指针的移动,既保证可以更新 新的最小值,也保证了不遗漏之前更新的最小值if dp[i] == next2:p2 += 1if dp[i] == next3:p3 += 1if dp[i] == next5:p5 += 1'''print(dp,p2,p3,p5) [1, 2, 1, 1, 1] 1 0 0[1, 2, 3, 1, 1] 1 1 0[1, 2, 3, 4, 1] 2 1 0[1, 2, 3, 4, 5] 2 1 15'''return dp[-1]
Java
class Solution {public int nthUglyNumber(int n) {int[] dp = new int[n];dp[0] = 1;int p2 = 0, p3 = 0, p5 = 0;for (int i = 1; i < n; ++i) {int next2 = dp[p2] * 2, next3 = dp[p3] * 3, next5 = dp[p5] * 5;dp[i] = Math.min(next2, Math.min(next3, next5));if (dp[i] == next2) ++p2;if (dp[i] == next3) ++p3;if (dp[i] == next5) ++p5;}return dp[n - 1];}
}
C++
class Solution {
public:int nthUglyNumber(int n) {vector<int> dp(n);dp[0] = 1;int p2 = 0, p3 = 0, p5 = 0;for (int i = 1; i < n; ++i) {int next2 = dp[p2] * 2, next3 = dp[p3] * 3, next5 = dp[p5] * 5;dp[i] = min(next2, min(next3, next5));if (dp[i] == next2) ++p2;if (dp[i] == next3) ++p3;if (dp[i] == next5) ++p5;}return dp[n - 1];}
};
Go
func nthUglyNumber(n int) int {dp := make([]int, n)dp[0] = 1p2, p3, p5 := 0, 0, 0for i := 1; i < n; i++ {next2, next3, next5 := dp[p2]*2, dp[p3]*3, dp[p5]*5dp[i] = min(next2, min(next3, next5))if dp[i] == next2 {p2++}if dp[i] == next3 {p3++}if dp[i] == next5 {p5++}}return dp[n-1]
}

http://www.ppmy.cn/devtools/110354.html

相关文章

qtdraw-使用qt绘图之开源源码学习

1. 资源介绍 功能&#xff1a;使用qt在画板上绘制各种形状&#xff0c;并保持绘制内容到xml文件中。 项目源码&#xff1a;https://github.com/egan2015/qdraw 软件界面&#xff1a; 1.1 支持shape 6种 1.2 支持的功能 6种&#xff0c;分别是对绘制的图形进行撤销undo&…

8K回报率10枚自定义按键,PC游戏丝滑操作,雷柏VT1 Pro MAX鼠标体验

现在喜欢玩PC游戏的朋友越来越多&#xff0c;《黑神话&#xff1a;悟空》等3A游戏也吸引到了更多的新玩家&#xff0c;想要完美运行此类游戏&#xff0c;不仅需要高性能的电脑硬件&#xff0c;还需要操控精准、反应敏捷的鼠标、键盘等外设。前端时间雷柏推出了VT1系列鼠标&…

【C++学习笔记】数组与指针(三)

目录 一、数组 1.1 数组声明与赋值 1.2 数组的特点 特点1&#xff1a;任意类型均可创建数组 特点2&#xff1a;固定大小 特点3&#xff1a;内存连续且有序 特点4&#xff1a;C无数组下标越界错误 特点5&#xff1a;数组变量不记录数据 1.3 遍历数组 普通for循环 for…

HarmonyOS开发之路由跳转

文章目录 一、路由跳转模式与实例1.router.pushUrl2.router.replaceUrl3.router.back 一、路由跳转模式与实例 跳转模式 有点类似于vue的路由跳转 router.pushUrl 保留路由栈&#xff0c;保留当前的页面&#xff1b;router.replaceUrl 销毁当前页面&#xff0c;跳转一个新的页…

【30天玩转python】文件操作

文件操作 Python 提供了一组强大且简单的文件操作功能&#xff0c;使得读写文件变得非常容易。通过 Python 的内置函数和标准库&#xff0c;我们可以方便地处理各种文件格式&#xff0c;如文本文件、二进制文件等。本节将详细介绍文件的基本操作&#xff0c;包括文件的打开、读…

游戏开发引擎___unity位置信息和unlit shader(无光照着色器)的使用,以桌子的渲染为例

unity是左手坐标系 1.位置信息 1.1 代码 using System.Collections; using System.Collections.Generic; using UnityEngine;public class positionTest : MonoBehaviour {public Camera Camera;private void OnGUI(){//世界坐标系&#xff0c;GUI里的标签GUI.Label(new Rec…

docker操作问题总结

登录 sudo docker login mogohub.tencentcloudcr.com --username 100002889649 --password eyJhbGciOiJSUzI1NiIsImtpZCI6IldOSFY6MjdCNTpGSlVOOlRPR1I6QlRYTDpZUldJOkNKNlI6U0pQVTpJRDJCOkU2UEY6SEwzRDozT0pGIn0.eyJvd25lclVpbiI6IjEwMDAwMjg4OTY0OSIsIm9wZXJhdG9yVWluIjoiMT…

Spring Boot 项目 Jar 包加密详解

一、为什么需要对 Jar 包进行加密&#xff1f; 1.1 防止代码被反编译 Java 字节码容易被反编译为源代码&#xff0c;反编译工具如 JD-GUI、CFR 等可以轻松查看 Jar 包中的代码逻辑。如果没有进行加密或混淆处理&#xff0c;攻击者可以轻易获取代码中的业务逻辑、算法和敏感数…