给你一个整数 n ,请你返回所有 0 到 1 之间(不包括 0 和 1)满足分母小于等于 n 的 最简 分数 。分数可以以 任意 顺序返回。
示例 1:
输入:n = 2
输出:["1/2"]
解释:"1/2" 是唯一一个分母小于等于 2 的最简分数。
示例 2:
输入:n = 3
输出:["1/2","1/3","2/3"]
示例 3:
输入:n = 4
输出:["1/2","1/3","1/4","2/3","3/4"]
解释:"2/4" 不是最简分数,因为它可以化简为 "1/2" 。
示例 4:
输入:n = 1
输出:[]
提示:
- 1 <= n <= 100
分析:
方法1:HashSet
因为分数化简后的分数也是最简分数,且分母小于当前分母,如果按分母大小升序存储的话,那么化简后的分数一定在前面,因此我们只需要定义一个集合来存储前面出现过的分数,每遍历一个分数就判断它是否在集合内,不在添加到结果集和该集合中,直到遍历完成。
时间复杂度:O(n^2)
空间复杂度:O(n^2)
class Solution {
public List<String> simplifiedFractions(int n) {
//结果集
List<String> res = new LinkedList<>();
//记录出现过的分数
HashSet<Double> set = new HashSet<>();
//遍历
//分母
for(int i = 2; i <= n; ++i){
//分子
for(int j = 1; j < i; ++j){
//记录分数
double frac = j / (double)i;
//集合中不包含该分数,添加该分数
if(!set.contains(frac)){
res.add(j +"/"+ i);
set.add(frac);
}
}
}
return res;
}
}
方法2:最大公约数
方法1的方式太占用内存,这道题的解题思路判断在于该分数是否能化简,而能否化简取决于它的最大公约数是否大于1,因此我们只需要判断分子分母的最大公约数是否大于1即可。
时间复杂度:O(n^3)
空间复杂度:O(1)
class Solution {
public List<String> simplifiedFractions(int n) {
//结果集
List<String> res = new LinkedList<>();
//遍历
//分母
for(int i = 2; i <= n; ++i){
//分子
for(int j = 1; j < i; ++j){
//判断是否不是最大公约数
if(isNotGCD(i, j)){
res.add(j +"/"+ i);
}
}
}
return res;
}
//判断是否不是最大公约数
public boolean isNotGCD(int i, int j){
//遍历,初始值取两数和差值的最小值
for(int k = Math.min(j, i-j); k > 1; k--){
//判断是否能被同时整除
if(i % k == 0 && j % k == 0){
return false;
}
}
return true;
}
}
方法3:最大公约数+欧几里德算法
方法2 的最大公约数比较耗时,我们可以用 欧几里德算法 进行优化。 欧几里德算法的思路就是两数取余,再用除数和余数取余,直到余数为0,此时的除数就为最大公约数。
时间复杂度:O(n^2*log n)
空间复杂度:O(1)
class Solution {
public List<String> simplifiedFractions(int n) {
//结果集
List<String> res = new LinkedList<>();
//遍历
//分母
for(int i = 2; i <= n; ++i){
//分子
for(int j = 1; j < i; ++j){
//判断最大公约数是否为1
if(getGCD(i, j) == 1){
res.add(j +"/"+ i);
}
}
}
return res;
}
//返回最大公约数
public int getGCD(int i, int j){
return i % j == 0 ? j: getGCD(j, i % j);
}
}
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/simplified-fractions