加载中...
1492-n 的第 k 个因子(The kth Factor of n)
发表于:2021-12-03 | 分类: 中等
字数统计: 1.7k | 阅读时长: 7分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/the-kth-factor-of-n

英文原文

Given two positive integers n and k.

A factor of an integer n is defined as an integer i where n % i == 0.

Consider a list of all factors of n sorted in ascending order, return the kth factor in this list or return -1 if n has less than k factors.

 

Example 1:

Input: n = 12, k = 3
Output: 3
Explanation: Factors list is [1, 2, 3, 4, 6, 12], the 3rd factor is 3.

Example 2:

Input: n = 7, k = 2
Output: 7
Explanation: Factors list is [1, 7], the 2nd factor is 7.

Example 3:

Input: n = 4, k = 4
Output: -1
Explanation: Factors list is [1, 2, 4], there is only 3 factors. We should return -1.

Example 4:

Input: n = 1, k = 1
Output: 1
Explanation: Factors list is [1], the 1st factor is 1.

Example 5:

Input: n = 1000, k = 3
Output: 4
Explanation: Factors list is [1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 125, 200, 250, 500, 1000].

 

Constraints:

  • 1 <= k <= n <= 1000

中文题目

给你两个正整数 n 和 k 。

如果正整数 i 满足 n % i == 0 ,那么我们就说正整数 i 是整数 n 的因子。

考虑整数 n 的所有因子,将它们 升序排列 。请你返回第 k 个因子。如果 n 的因子数少于 k ,请你返回 -1 。

 

示例 1:

输入:n = 12, k = 3
输出:3
解释:因子列表包括 [1, 2, 3, 4, 6, 12],第 3 个因子是 3 。

示例 2:

输入:n = 7, k = 2
输出:7
解释:因子列表包括 [1, 7] ,第 2 个因子是 7 。

示例 3:

输入:n = 4, k = 4
输出:-1
解释:因子列表包括 [1, 2, 4] ,只有 3 个因子,所以我们应该返回 -1 。

示例 4:

输入:n = 1, k = 1
输出:1
解释:因子列表包括 [1] ,第 1 个因子为 1 。

示例 5:

输入:n = 1000, k = 3
输出:4
解释:因子列表包括 [1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 125, 200, 250, 500, 1000] 。

 

提示:

  • 1 <= k <= n <= 1000

通过代码

高赞题解

因为题目的数据规模非常小,所以暴力枚举从 1 到 n 的每一个数字,看是不是 n 的因子,就可以通过(C++)。

class Solution {
public:
    int kthFactor(int n, int k) {

        vector<int> factors;
        for(int i = 1; i <= n; i ++)
            if(n % i == 0) factors.push_back(i);
        return k - 1 < factors.size() ? factors[k - 1] : -1;
    }
};

上述算法时间是 O(n) 的,空间是 O(n) 的。


很多同学都知道,求解一个数字的所有因子,使用 O(sqrt(n)) 的时间就可以。

这是因为,当我们知道 d 是 n 的因子的时候,就知道了 n / d 也是 n 的因子。所以,我们只需要从 1 搜索到 sqrt(n) 就足够了。

但是因为这个问题最后要将所有因子按照升序排列,因此,以下代码虽然在 O(sqrt(n)) 的时间里完成了查找 n 的所有因子,但最终排序反而让复杂度升高了。

不过通过以下代码,同学们可以先回顾一下如何使用 O(sqrt(n)) 的时间找到一个数字 n 的所有因子(C++)。

注释的两个地方是容易出错的地方.

class Solution {
public:
    int kthFactor(int n, int k) {

        vector<int> factors;

        // 使用 i * i <= n,避免 sqrt(n) 运算的性能和精度问题
        for(int i = 1; i * i <= n; i ++) 
            if(n % i == 0){
                factors.push_back(i);
                
                // 对于 i * i == n 的情况要进行一下判断,
                // 如果 i * i == n,则 i 和 n / i 是一个数字,不能重复添加进 factors
                if(i * i != n) 
                    factors.push_back(n / i);
            }

        // 需要排序
        sort(factors.begin(), factors.end());
        return k - 1 < factors.size() ? factors[k - 1] : -1;
    }
};

下面我们可以考虑一下,怎么能避免排序的过程?

其实非常简单。我们在从 i = 1 遍历到 i * i <= n 的过程中,找到的每一个因子 i 都是有序的。所以,如果我们要找的第 k 个因子在这个范围里,可以直接返回。

与此同时,我们找到的每一个因子 n / i 都是倒序的,所以,我们倒序将 n / i 这些因子存在数组中,然后,根据 k 找到它在倒序的数组中出现的位置即可。

以下为我的参考代码(C++):

class Solution {
public:
    int kthFactor(int n, int k) {

        vector<int> factors;
        for(int i = 1; i * i <= n; i ++)
            if(n % i == 0){
                k --; // 每次 k --
                if(!k) return i; // 如果此时 k == 0,就已经找到了这个因子,就是 i

                // 倒序存储 n / i。注意,我们还需要对 i * i == n 的情况做判断
                if(i * i != n) 
                    factors.push_back(n / i);
            }

        // 最终,因为 factors 是倒序的
        // 我们只需要看在 factors 数组中倒数第 k 个元素就好了
        return k - 1 < factors.size() ? factors[factors.size() - k] : -1;
    }
};

上述算法时间是 O(sqrt(n)) 的,空间是 O(sqrt(n)) 的。因为 factors 最多存储了 sqrt(n) 个约数。

另外,上述算法已经进行了优化。由于我们知道 factors 是倒序的,可以使用线性时间将 factors 翻转过来。因为 factors 里最多有 sqrt(n) 个元素,所以我们可以使用 O(sqrt(n)) 的时间翻转,之后再用 O(1) 的时间拿到第 k 个元素。

这个方法,时间和空间也都是 O(sqrt(n)),但是多了一步翻转数组的过程。


问题来了,我们有没有可能使用 O(1) 的空间完成这个问题?

答案是可以的!但是,我们需要两趟 O(sqrt(n)) 的遍历。

第一趟,从 i = 1 遍历到 i * i <= n,这个过程是在看 i 这个因子;

然后,反向从 i * i > n 的那个 i,遍历回 1,这个过程,再看 n / i 这个因子。

以下为我的参考代码(C++):

class Solution {
public:
    int kthFactor(int n, int k) {

        int i;
        for(i = 1; i * i <= n; i ++)
            if(n % i == 0){
                k --;
                if(!k) return i;
            }

        i--; // 注意,此时 i * i > n,所以要 i --

        // 第二趟反向遍历,对 i 的初始值,还需要根据是否 i * i == n 做判断,避免重复
        for(i = (i * i == n ? i - 1 : i); i > 0; i --)
            if(n % i == 0){
                k --;
                if(!k) return n / i; // 看 n / i
            }

        return -1;
    }
};

上述算法时间是 O(sqrt(n)) 的,空间是 O(1) 的。

是不是很酷?:)


觉得有帮助请点赞哇!

统计信息

通过次数 提交次数 AC比率
7502 11477 65.4%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
1473-粉刷房子 III(Paint House III)
下一篇:
1493-删掉一个元素以后全为 1 的最长子数组(Longest Subarray of 1's After Deleting One Element)
本文目录
本文目录