统计数字n以内的质数,找到第n个丑数, 相似点
统计数字n以内的质数,找到第n个丑数,组成n的完全平方数的最少个数,相似点
找到第n个丑数,合并k个排序链表
统计所有小于非负整数 n 的质数的数量。
示例:
输入: 10
输出: 4
解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
参考:
作者:labuladong
链接:https://leetcode-cn.com/problems/count-primes/solution/ru-he-gao-xiao-pan-ding-shai-xuan-su-shu-by-labula/
来源:力扣(LeetCode)
class Solution {
public int countPrimes(int n) {
int res = 0;
for(int i = 2; i < n; i++) {
if(isPrime(i)) res++;
}
return res;
}
private boolean isPrime(int num) {
for(int i = 2; i < num; i++) {
if(num % i == 0) return false;
}
return true;
}
}
public int countPrimes(int n) {
int count = 0;
for (int i = 1; i < n; i++) {
if (isPrime(i)) count++;
}
return count;
}
private boolean isPrime(int num) {
if (num <= 1) return false;
// Loop's ending condition is i * i <= num instead of i <= sqrt(num)
// to avoid repeatedly calling an expensive function sqrt().
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) return false;
}
return true;
}
int countPrimes(int n) {
boolean[] isPrim = new boolean[n];
// 将数组都初始化为 true
Arrays.fill(isPrim, true);
for (int i = 2; i < n; i++)
if (isPrim[i])
// i 的倍数不可能是素数了
for (int j = 2 * i; j < n; j += i)
isPrim[j] = false;
int count = 0;
for (int i = 2; i < n; i++)
if (isPrim[i]) count++;
return
for (int j = 2 * i; j < n; j += i)
isPrim[j] = false;
可以把 i 的整数倍都标记为 false,但是仍然存在计算冗余。
比如 n = 25,i = 4 时算法会标记 4 × 2 = 8,4 × 3 = 12 等等数字,但是这两个数字已经被 i = 2 和 i = 3 的 2 × 4 和 3 × 4 标记了。
我们可以稍微优化一下,让 j 从 i 的平方开始遍历,即规定每个数从大于等于i的i倍开始,而不是从 2 * i 开始:
例如: 5 × 2 = 10 , 5的2倍,也是2的5倍; 5 × 3 = 15,5的3倍,也是3的6倍,所以从5的5倍开始,不要前面重复的。
for (int j = i * i; j < n; j += i)
isPrim[j] = false;
// 有点dp的感觉
// 先让boolean[] isPrime = new boolean[] 数组全部是true
// 只要是质数,那么他的整数倍就一定不是质数
// 优化1:只需要判断 [2 ~ sqrt(n)]
// 优化2:整数倍判断会有重复,从 j = i * i 判断
int countPrimes(int n) {
boolean[] isPrim = new boolean[n];
Arrays.fill(isPrim, true);
for (int i = 2; i * i < n; i++)
if (isPrim[i])
for (int j = i * i; j < n; j += i)
isPrim[j] = false;
int count = 0;
for (int i = 2; i < n; i++)
if (isPrim[i]) count++;
return count;
}
如果写成i * i < n,结果乘以2倍,出现以下错误:因为 2 * 2 < 3, 不会进入循环,结果为0;但其实结果为1;
编写一个程序,找出第 n 个丑数。
丑数就是质因数只包含 2, 3, 5 的正整数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
class Solution {
public int nthUglyNumber(int n) {
// 从1开始判断,到第n个为止
int count = 1;
int i = 1;
while(count <= n) {
if(isUglyNumber(i)) {
count++;
}
i++;
}
return i - 1; // 第n个数之后,i还会自增一次,所以要减1
}
private boolean isUglyNumber(int num) {
// 不断去除2,3,5,剩下的余数是1则是丑数,不是1则不是丑数
int[] factors = new int[]{
2, 3, 5};
for(int factor : factors) {
while(num % factor == 0) {
num = num / factor;
}
}
return num == 1;
}
}
自底向上
要用Long,不然会报错
class Solution {
public int nthUglyNumber(int n) {
// 一个一个判断找丑数太耗时,因为很多数不是丑数
// 类似dp思想,自底向上;和寻找质数的思想,质数的整数倍肯定不是质数,丑数的2,3,5倍肯定是丑数
// 关键: 从小到大,要找第n个丑数,找第n个,就想到堆
// 类似于合并k个链表思想:将最小的元素添加进去,再弹出最小的元素,以该元素作为基底,再去乘以2,3,5作为新的丑数
// 添加到堆中,再弹出第二小的丑数,再以他为基础...
// 即弹出的最小的,以他为基础的,肯定是第二小的
// 这里还多涉及一个去重问题, 整数倍的整数之间肯定存在重复,导致弹出第i个最小堆顶元素后,此时本是第i+1个最小元素,但是是重复的
Queue<Long> queue = new PriorityQueue<Long>();
int count = 0;
long uglyNum = 1;
queue.add(uglyNum);
int[] factors = new int[]{
2, 3, 5};
while(count < n) {
if(queue.size() > 0) {
uglyNum = queue.poll();
count++;
}
while(queue.size() > 0 && queue.peek() == uglyNum) {
queue.poll();
}
for(int factor : factors) {
queue.add(factor * uglyNum);
}
}
return (int) uglyNum;
}
}
class Solution {
public int nthUglyNumber(int n) {
// 自底向上
// 堆排序
// 去重
TreeSet<Long> set = new TreeSet<Long>();
long uglyNum = 1;
set.add(uglyNum);
int count = 0;
int[] factors = new int[]{
2, 3, 5};
while(count < n && set.size() > 0) {
uglyNum = set.pollFirst();
count++;
for(int factor : factors) {
set.add(factor * uglyNum);
}
}
return (int) uglyNum;
}
}
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例 1:
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9.
class Solution {
public int numSquares(int n) {
int[] dp = new int[n+1];
for(int i = 1; i <= n; i++) {
dp[i] = i;
}
for(int i = 1; i <= n; i++) {
for(int j = 1; i - j*j >= 0; j++) {
dp[i] = Math.min(dp[i - j*j] + 1, dp[i]);
}
}
return dp[n];
}
}
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
// 堆来排序
// 每次弹出最小的元素,依次next
Queue<ListNode> queue = new PriorityQueue<>((n1, n2) -> n1.val - n2.val);
int k = lists.length;
for(int i = 0; i < k; i++) {
if(lists[i] != null) {
queue.add(lists[i]);
}
}
ListNode prev = new ListNode(-1);
ListNode curr = prev;
while(queue.size() > 0) {
ListNode minNode = queue.poll();
curr.next = minNode;
curr = curr.next;
if(minNode.next != null) {
queue.add(minNode.next);
}
}
return prev.next;
}
}