leetcode.com/problems/the-kth-factor-of-n/
The kth Factor of n - LeetCode
Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.
leetcode.com
금요일이라고 쉬운문제인건가 싶었지만, 그렇지 않았음
In this article, we consider three solutions.
Brute Force, \mathcal{O}(N)O(N). One could iterate from 11 to NN, figure out all divisors in a linear time, and then return the kth one.
Fig. 1. Approach 1: Brute Force, \mathcal{O}(N)O(N). The idea is to iterate from 1 to N to figure out all divisors.
The implementation of the next two approaches is based on the same idea.
The divisors are paired, i.e., if ii is a divisor of NN, then N / iN/i is a divisor of NN, too. That means it's enough to iterate up to \sqrt{N}N.
Fig. 2. Iterate from 1 to \sqrt{N}N is enough.
Heap, \mathcal{O}(\sqrt{N} \log k)O(Nlogk). The idea is to iterate from 1 to \sqrt{N}N, and push each divisor and its pair into max heap of size kk.
Fig 3. Approach 2. Heap: Iterate from 1 to \sqrt{N}N is enough, and push each divisor and its pair into max heap.
Math, \mathcal{O}(\sqrt{N})O(N). As in Approach 2, let's iterate up to \sqrt{N}N, and decrease kk by one at each step. If kk drops down to zero during the iterations - the kth divisor is here. Otherwise, the kth divisor is the paired one and could be found as N / divisors[len(divisors) - k].
Fig. 4. Approach 3. Math: Iterate from 1 to \sqrt{N}N is enough.
Brute Force, O(N) time
Fig. 5. Approach 1: Brute Force.
Algorithm
Iterate by xx from 11 to N / 2:
Return NN if k == 1k==1 and -1−1 otherwise.
class Solution {
public int kthFactor(int n, int k) {
for (int x = 1; x < n / 2 + 1; ++x) {
if (n % x == 0) {
--k;
if (k == 0) {
return x;
}
}
}
return k == 1 ? n : -1;
}
}
이 코드와 위의 코드는 많은 차이가 있다.
n까지 모두 도는게, 숫자가 크면 시간이 오래 걸린다. n/2+1 까지만 돌아야함.
그리고 위의 코드는 n까지 돌지 않기 때문에, 마지막 리턴에서 아래에서 n에 대한것을 따로 처리해주어야한다.
class Solution {
public int kthFactor(int n, int k) {
for(int i = 1; i <= n; i++) {
if(n%i == 0) {
k--;
if(k==0) {
return i;
}
}
}
return -1;
}
}
Implementation
Complexity Analysis
Time Complexity: O(N) to iterate from 11 to NN.
Space Complexity: O(1), since we don't allocate any additional data structures.
Heap, \mathcal{O}(\sqrt{N} \times \log{k})O(N×logk) time
Fig 6. Approach 2: Heap.
Algorithm
Initialize max heap. Use PriorityQueue in Java and heap in Python. heap is a min-heap. Hence, to implement max heap, change the sign of divisor before pushing it into the heap.
Iterate by xx from 11 to \sqrt{N}N:
Return the head of the heap if its size is equal to kk and -1−1 otherwise.
힙에다가 원소를 추가할 때, k개수보다 넘는것은 뒤에서 부터 삭제한다.
class Solution {
// max heap -> to keep max element always on top
Queue<Integer> heap = new PriorityQueue<>((o1, o2) -> o2 - o1);
// push into heap
// by limiting size of heap to k
public void heappushK(int x, int k) {
heap.add(x);
if (heap.size() > k) {
heap.remove();
}
}
public int kthFactor(int n, int k) {
int sqrtN = (int) Math.sqrt(n);
for (int x = 1; x < sqrtN + 1; ++x) {
if (n % x == 0) {
heappushK(x, k);
if (x != n / x) {
heappushK(n / x, k);
}
}
}
return k == heap.size() ? heap.poll() : -1;
}
}
Implementation
Complexity Analysis
Time Complexity: \mathcal{O}(\sqrt{N} \times \log k)O(N×logk).
Space Complexity: \mathcal{O}(\min(k, \sqrt{N}))O(min(k,N)) to keep the heap of size kk.
Math, \mathcal{O}(\sqrt{N})O(N) time
Fig. 7. Approach 3: Math.
Algorithm
Initialize a list divisors to store the divisors.
Iterate by xx from 11 to \sqrt{N}N:
We're here because the kth divisor is not yet found. Although divisors already contains all "independent" divisors. All other divisors are "paired" ones, i.e, the kth divisor could be computed as N / divisors[len(divisors) - k].
But before that, we need a small correction for the case when NN is a perfect square. In that case, the divisor list contains a duplicate because \sqrt{N}N appears two times. To skip it, we have to increase kk by one.
Return N / divisors[len(divisors) - k] if k <= len(divisors) and -1 otherwise.
class Solution {
public int kthFactor(int n, int k) {
List<Integer> divisors = new ArrayList();
int sqrtN = (int) Math.sqrt(n);
for (int x = 1; x < sqrtN + 1; ++x) {
if (n % x == 0) {
--k;
divisors.add(x);
if (k == 0) {
return x;
}
}
}
// If n is a perfect square
// we have to skip the duplicate
// in the divisor list
if (sqrtN * sqrtN == n) {
++k;
}
int nDiv = divisors.size();
return (k <= nDiv) ? n / divisors.get(nDiv - k) : -1;
}
}
Implementation
Complexity Analysis
Time Complexity: \mathcal{O}(\sqrt{N})O(N).
Space Complexity: \mathcal{O}(\min(k, \sqrt{N}))O(min(k,N)) to store the list of divisors.