1447. 最简分数 : 简单数论运用题(求 gcd 几种方式)

简介: 1447. 最简分数 : 简单数论运用题(求 gcd 几种方式)

网络异常,图片无法展示
|


题目描述



这是 LeetCode 上的 1447. 最简分数 ,难度为 中等


Tag : 「数学」、「最大公约数」


给你一个整数 n ,请你返回所有 0011 之间(不包括 0011)满足分母小于等于  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 <= 1001<=n<=100


数论



数据范围为 100100 且数值大小在 (0, 1)(0,1) 之间,因此枚举「分子 + 分母」的 O(n^2)O(n2) 做法是可接受的。


于是问题转化为:如何快速判断两个数组成的分数是否为最简(即判断两个数的最大公约数是否为 11)。


快速求得 aabb 的最大公约数的主要方式有两种 :「更相减损法」和「欧几里得算法」,其中「欧几里得算法」的递归实现最为好写,复杂度为 O(\log{(a + b)})O(log(a+b)),在绝大多数的情况下适用,只有在需要实现高精度时,才会考虑使用「更相减损法」。


而 stein 算法则是没有必要掌握的。


代码:


class Solution {
    int gcd(int a, int b) { // 欧几里得算法
        return b == 0 ? a : gcd(b, a % b);
    }
    public List<String> simplifiedFractions(int n) {
        List<String> ans = new ArrayList<>();
        for (int i = 1; i < n; i++) {
            for (int j = i + 1; j <= n; j++) {
                if (gcd(i, j) == 1) ans.add(i + "/" + j);
            }
        }
        return ans;
    }
}
复制代码

class Solution {
    int gcd(int a, int b) { // 更相减损法
        while (true) {
            if (a > b) a -= b;
            else if (a < b) b -= a;
            else return a;
        }
    }
    public List<String> simplifiedFractions(int n) {
        List<String> ans = new ArrayList<>();
        for (int i = 1; i < n; i++) {
            for (int j = i + 1; j <= n; j++) {
                if (gcd(i, j) == 1) ans.add(i + "/" + j);
            }
        }
        return ans;
    }
}
复制代码

class Solution {
    int gcd(int a, int b) { // stein
        if (a == 0 || b == 0) return Math.max(a, b);
        if (a % 2 == 0 && b % 2 == 0) return 2 * gcd(a >> 1, b >> 1);
        else if (a % 2 == 0) return gcd(a >> 1, b);
        else if (b % 2 == 0) return gcd(a, b >> 1);
        else return gcd(Math.abs(a - b), Math.min(a, b));
    }
    public List<String> simplifiedFractions(int n) {
        List<String> ans = new ArrayList<>();
        for (int i = 1; i < n; i++) {
            for (int j = i + 1; j <= n; j++) {
                if (gcd(i, j) == 1) ans.add(i + "/" + j);
            }
        }
        return ans;
    }
}
复制代码


  • 时间复杂度:枚举分子分母的复杂度为 O(n^2)O(n2);判断两数是否能凑成最简分数复杂度为 O(\log{n})O(logn)。整体复杂度为 O(n^2 * \log{n})O(n2logn)
  • 空间复杂度:忽略递归带来的额外空间开销,复杂度为 O(1)O(1)


最后



这是我们「刷穿 LeetCode」系列文章的第 No.1447 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。


在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。


为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour…


在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

相关文章
|
机器学习/深度学习
3045 Lcm与Gcd构造
已知: gcd(a,b) = n lcm(a,b) = m 求min(a,b)是多少 通过gcd的了解我们可以知道,两个数a == k1 * n以及b == k2 * n并且gcd(k1,k2) == 1 ab == n * m m == a * b/n ab == k1 * k2 * n * n 于是可以得到 m == k1 * k2 * n 将n除到左边,可以得出m/n == k1 * k2 于是k1 和 k2 都是 m / n的因子 这样就可以以根号的复杂度找出这两个因子,并判断k1 和 k2 是否是互质的 a + b == (k1 + k2 ) * n 所以说代码:
122 0
|
数据库 iOS开发
ios多线程-GCD基本用法
ios中多线程有三种,NSTread, NSOperation,GCD 这篇就讲讲GCD的基本用法
|
算法
gcd算法
原理: 两个整数的最大公约数等于其中较小的数和两数的差的最大公约数。即一步步的降低两个数的值,直到其中一个变成零,这时所剩下的还没有变成零的数就是两个数的最大公约数。
1410 0
|
移动开发 调度
GCD总结(一)
GCD为我们提供了三种类型的调度队列(dispatch queue),分别为串行,并行和主调度队列。     串行(Serial)     你可以创建任意个数的串行队列,每个队列依次执行添加的任务,一个队列同一时刻只能执行一个任务(串行),但是各个队列之间不影响,可以并发执行。
576 0
|
网络性能优化 Swift Go
GCD
什么是 GCD ? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯 C 语言,提供了非常多强大的函数 GCD的优势: GCD 是苹果公司为多核的并行运算提出的解决方案; GCD 会自动利用更多的 CPU 内核(比如...
1000 0
ex_gcd(个人模版)
ex_gcd: 1 #include 2 #include 3 using namespace std; 4 int x,y; 5 int ex_gcd(int a,int b,int &x,int &y) 6 { 7 if(b==0) 8 { ...
836 0
|
Java
GCD源码分析
# 背景 最近在浏览React Native代码的时候发现有提到Main Queue和Main Thread的区别,很早就有阅读GCD源码的冲动,这回总算找到机会了。 阅读源码之前先给个结论:Main Thread 和 Main Queue是两个不同的东西。 + Main Queue IS bound to Main Thread. + Main Thread IS NOT bou
8093 0