【LeetCode】丑数题目合辑

文章目录

  • 263. 丑数
      • 思路
      • 代码
  • 264. 丑数 II
    • 方法一:最小堆
      • 思路
      • 代码
    • 方法二:动态规划(三指针法)
      • 思路
      • 代码
  • 1201. 丑数 III
    • 方法:二分查找 + 容斥原理
      • 思路
      • 代码
  • 313. 超级丑数
    • 方法:“多路归并”
      • 思路
      • 代码
  • 总结
  • 参考资料

263. 丑数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路

  • 首先,丑数必须是正整数,因此对于 n<1 都可以直接返回 false;
  • 对于 n >= 1 ,如果 n 能够被 2/3/5 整除,说明它们是丑数。

代码

class Solution {
public:bool isUgly(int n) {// ugly只能是正整数if(n < 1) return false;vector<int> factors = {2, 3, 5};for(int i=0; i<=2; ++i){while(n % factors[i] == 0){n /= factors[i];}}return n == 1;}
};

264. 丑数 II

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

方法一:最小堆

思路

  • 要得到第 n 个丑数,可以使用最小堆实现。
  • 初始化堆为空,首先将最小的丑数 1 加入。每次取出堆顶元素 x ,则 x 是堆中最小的丑数,2x、3x、5x 必然也是丑数,因此将它们也加入最小堆。
  • 但是上述做法会出现重复元素,为了避免这种情况,用哈希集合去重,避免相同元素多次加入堆。
  • 在排除重复元素的情况下,第 n 次从最小堆中取出的元素即为第 n 个丑数。

代码

class Solution {
public:int nthUglyNumber(int n) {vector<int> factor = {2, 3, 5};priority_queue<long, vector<long>, greater<long>> heap;unordered_set<long> s;// 先把丑数 1 加入s.insert(1L);heap.push(1L);long cur;for(int i=0; i<n; ++i){cur = heap.top();heap.pop();for(int f : factor){// 依次计算 2x 3x 5xlong temp = cur * f;// s中没有temp 将其存入if(!s.count(temp)){s.insert(temp);heap.push(temp);}}}return (int)cur;}
};

方法二:动态规划(三指针法)

思路

  • 方法一使用最小堆,会预先存储较多的丑数,维护最小堆的过程也导致时间复杂度较高。可以使用动态规划的方法进行优化。

  • 定义数组 dp,其中 dp[i] 表示第 i 个丑数,则 dp[n] 为这道题的答案。其中,dp[1] = 1。

  • 剩余的丑数我们可以通过三个指针 p2 、p3、p5 计算得到。pi 的含义是有资格同 i 相乘的最小丑数的位置。这里资格指的是:如果一个丑数nums[pi]通过乘以 i 可以得到下一个丑数,那么这个丑数nums[pi]就永远失去了同 i 相乘的资格,我们把pi++,让 nums[pi] 指向下一个丑数即可。

  • 举例说明:

    一开始,丑数只有{1},1 可以同 2,3,5相乘,取最小的 1×2=2 添加到丑数序列中。

    现在丑数中有{1,2},在上一步中,1 已经同 2 相乘过了,所以今后没必要再比较 1×2 了,因此认为 1 失去了同 2 相乘的资格。

    现在 1 有与 3,5 相乘的资格,2 有与 2,3,5 相乘的资格,但是 2×3 和 2×5 肯定比 1×3 和 1×5 大,因此没必要比较。所以我们只需要比较 1×3,1×5,2×2

    依此类推,每次我们都分别比较有资格同 2,3,5 相乘的最小丑数,选择最小的那个作为下一个丑数,假设选择到的这个丑数是同 i(i=2,3,5)相乘得到的,所以它失去了同 i 相乘的资格,把对应的pi++,让 pi 指向下一个丑数即可。

代码

class Solution {
public:int nthUglyNumber(int n) {vector<int> dp(n+1);dp[1] = 1;int p2 = 1, p3 = 1, p5 = 1;for(int i=2; i<=n; ++i){int num2 = 2 * dp[p2];int num3 = 3 * dp[p3];int num5 = 5 * dp[p5];dp[i] = min(min(num2, num3), num5);// 确定dp[i]是由哪个数字生成的if(dp[i] == num2)   p2++;if(dp[i] == num3)   p3++;if(dp[i] == num5)   p5++;}return dp[n];}
};

1201. 丑数 III

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

方法:二分查找 + 容斥原理

思路

  • 首先要注意的是,这道题和前两题对于“丑数”的定义并不相同。这里不能直接枚举丑数 x(三指针法),因为 x 太大了,会导致超时。

  • 这道题是 878. 第 N 个神奇数字 的升级版。把两个数改为了三个数,难度更高。与 878 题相比,这题的只需要修改求小于等于 x 的丑数个数的函数,二分查找部分一模一样。建议先复习 878 题,下面附上该题的思路图。

    在这里插入图片描述

  • 把小于等于 x 的 a 的倍数、b 的倍数、c 的倍数组成的集合分别叫做 A、B、C ,小于等于 x 的丑数相当于集合 A∪B∪C ,集合的元素个数为 ∣A∪B∪C∣,由 容斥原理可得:

    ∣A∪B∪C∣=∣A∣+∣B∣+∣C∣−∣A∩B∣−∣A∩C∣−∣B∩C∣+∣A∩B∩C∣

  • 因此,小于等于 x 的丑数的个数为:x/a + x/b + x/c - x/lcm_a_b - x/lcm_b_c - x/lcm_a_c + x/lcm_a_b_c 。可以使用最小公倍数函数 std::lcm

  • 接下来通过二分搜索,不断缩小边界,直到某个位置所对应的数恰好包含了 n 个丑数因子为止。

代码

class Solution {
public:using ll = long long;ll nthUglyNumber(ll n, ll a, ll b, ll c) {ll lcm_a_b = std::lcm(a, b), lcm_a_c = std::lcm(a, c), lcm_b_c = std::lcm(b, c);ll lcm_a_b_c= std::lcm(lcm_a_b, c);// 最小的丑数必然是a、b、c的最小值ll left = min(min(a, b), c);ll right = n * left;while(left + 1 < right){ll mid = (left + right) / 2;if(mid/a + mid/b + mid/c - mid/lcm_a_b - mid/lcm_a_c - mid/lcm_b_c + mid/lcm_a_b_c < n){left = mid;}else right = mid;}return right;}
};

313. 超级丑数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

方法:“多路归并”

思路

  • 这道题其实是 264. 丑数 II 的进阶,前者固定使用三个指针,分别对应于 2、3、5,而这道的primes数组长度不固定,因此使用指针数组来对应 primes 的每一个值。

  • 第一个丑数一定是 1,而「往后产生的丑数」都是基于「已有丑数」而来(使用「已有丑数」乘上「给定质因数」primes[i] )。具体过程如图所示。

    在这里插入图片描述

    • 显然,我们需要每次取值最小的一个,然后让指针后移(指向下一个丑数),不断重复这个过程,直到找到第 n 个丑数。
    • 另外,由于我们每个指针移动和 dp的构造,都是单调递增,因此可以通过与 dp[i-1] 进行比较来实现去重,而无须引用 Set 结构。

代码

class Solution {
public:long nthSuperUglyNumber(int n, vector<int>& primes) {int len = primes.size();// 指针数组vector<long> ptr(len, 1);vector<long> dp(n+1, 0);dp[1] = 1;for(int i=2;i<=n;++i){int flag = 0;dp[i] = primes[0] * dp[ptr[0]];for(int j=0; j<len; ++j){long cur = primes[j] * dp[ptr[j]];if(cur < dp[i]){flag = j;dp[i]= cur;}     }ptr[flag]++;// 如果当前值和上一个丑数一样,那么跳过该丑数if(dp[i] == dp[i-1]) i--;}return dp[n];}
};

总结

  • 以上是丑数的所有相关题目,丑数的定义并不固定,因此需要仔细理解题意后再开始计算。
  • 对于 263. 丑数,运用简单的数学思想即可解决;
  • 对于264. 丑数II ,使用了最小堆动态规划(即三指针的方法),但是最小堆的方法比较费时,更推荐方法二;
  • 对于1201. 丑数III ,与一般的丑数定义不同,是 878. 第 N 个神奇数字 的升级版,使用了二分查找容斥原理
  • 对于313.超级丑数 ,是 264. 丑数II 的升级版,将三指针方法扩展为多指针即可求解,也可以理解为多路归并法

参考资料

  1. 丑数 II 官方题解
  2. 三指针方法的理解方式
  3. 313. 超级丑数:【宫水三叶】一题双解 :「优先队列」&「多路归并」

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PCIe接口的PCB布局布线要求

PCI-Express&#xff0c;简称“PCI-e”是一种高速串行计算机扩展总线标准&#xff0c;PCI-E属于高速串行点对点双通道高带宽传输&#xff0c;所连接的设备分配独享通道带宽&#xff0c;不共享总线带宽&#xff0c;它的主要优势就是数据传输速率高。 PCI-E2.0和PCI-E3.0主要存在…

Element Plus报错:ResizeObserver loop completed with undelivered notifications.

el-selected踩坑&#xff1a;el-selected 显示下拉框 mouseover 时报错&#xff01;&#xff01;&#xff01; 原来是属性 popper-append-to-body 被废除&#xff0c;改为 teleported。 element ui <el-select:popper-append-to-body"false"value-key"id&q…

uniapp 使用canvas画海报(微信小程序)

效果展示&#xff1a; 项目要求&#xff1a;点击分享绘制海报&#xff0c;并实现分享到好友&#xff0c;朋友圈&#xff0c;并保存 先实现绘制海报 <view class"data_item" v-for"(item,index) in dataList" :key"index"click"goDet…

导出微信好友,批量加好友、导出微信群员信息

下面的代码基于千寻框架&#xff0c;都测试过了&#xff0c;都能正常使用。 需要项目代码或者千寻框架的可以加我V&#xff1a;iostreamX64 注意&#xff1a;批量加好友的时候记得设置个时间间隔&#xff0c;加好友太频繁微信会被封的。 导出微信群员信息 import pandas as…

arm:day2

.text 文本段 .global _start 声明一个_start全局函数的入口 _start: _start标签&#xff0c;就是c语言的函数/* mov r0,#9 r09mov r1,#15 r115 loop: cmp r0,r1 比较r0和r1 beq stop 如果r0等于r1&#xff0c;跳转stopsubhi r0,r0,r1 如果r0大于r1&#xff0c;…

Docker查看、创建、进入容器相关的命令

1.查看、创建、进入容器的指令 用-it指令创建出来的容器&#xff0c;创建完成之后会立马进入容器。退出之后立马关闭容器。 docker run -it --namec1 centos:7 /bin/bash退出容器&#xff1a; exit查看现在正在运行的容器命令&#xff1a; docker ps查看历史容器&#xff0…

解决hbase节点已下线,但在status中显示为dead问题

工作中需要下线4台hbase小节点&#xff0c;下线完成后使用status 命令查看,有一台为dead状态: 使用status detailed 查看&#xff0c;发现“hd-03"这台节点是dead。 检查各节点配置文件无误&#xff0c;并使用 /opt/hbase/bin/hbase-daemon.sh restart master 重启两个…

kafka基本概念及操作

kafka介绍 Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的 &#xff08;replica&#xff09;&#xff0c;基于zookeeper协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各…

java毕业设计-智慧食堂管理系统-内容快览

首页 智慧食堂管理系统是一种可以提高食堂运营效率的管理系统。它将前端代码使用Vue实现&#xff0c;后端使用Spring Boot实现。这个系统的目的是简化食堂管理&#xff0c;提高食堂服务质量。在现代快节奏的生活中&#xff0c;人们对餐饮服务提出了更高的要求&#xff0c;食堂管…

457. 环形数组是否存在循环

457. 环形数组是否存在循环 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;经验吸取 原题链接&#xff1a; 457. 环形数组是否存在循环 https://leetcode.cn/problems/circular-array-loop/description/ 完成情况&#xff1a; 解题思路…

undefined reference to `dlopen‘ ‘SSL_library_init‘ `X509_certificate_type‘

使用Crow的时候需要注意crow依赖asio依赖OpenSSL&#xff0c;asio要求1.22以上版本&#xff0c;我使用的是1.26.0&#xff1b; 这个版本的asio要求OpenSSL是1.0.2&#xff0c;其他版本我得机器上编不过&#xff0c;ubuntu上默认带的OpenSSL是1.1.1; 所以我下载了OPENSSL1.2.0重…

图像变形之移动最小二乘算法(MLS)

基本原理 基于移动最小二乘的图像变形是通过一组源控制点和目标控制点来控制变形&#xff0c;对于每一个待求变形后位置的点而言&#xff0c;根据预设的形变类型&#xff08;如仿射变换、相似变换、刚性变换&#xff09;求解一个最小二乘优化目标函数估计一个局部的坐标变换矩阵…