Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
You are given a sorted integer array arr containing 1 and prime numbers, where all the integers of arr are unique. You are also given an integer k.
For every i and j where 0 <= i < j < arr.length, we consider the fraction arr[i] / arr[j].
Return the kth smallest fraction considered. Return your answer as an array of integers of size 2, where answer[0] == arr[i] and answer[1] == arr[j].
Example 1:
Input: arr = [1,2,3,5], k = 3 Output: [2,5] Explanation: The fractions to be considered in sorted order are: 1/5, 1/3, 2/5, 1/2, 3/5, and 2/3. The third fraction is 2/5.
Example 2:
Input: arr = [1,7], k = 1 Output: [1,7]
Constraints:
2 <= arr.length <= 10001 <= arr[i] <= 3 * 104arr[0] == 1arr[i] is a prime number for i > 0.arr are unique and sorted in strictly increasing order.1 <= k <= arr.length * (arr.length - 1) / 2O(n2) complexity?Problem summary: You are given a sorted integer array arr containing 1 and prime numbers, where all the integers of arr are unique. You are also given an integer k. For every i and j where 0 <= i < j < arr.length, we consider the fraction arr[i] / arr[j]. Return the kth smallest fraction considered. Return your answer as an array of integers of size 2, where answer[0] == arr[i] and answer[1] == arr[j].
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Two Pointers · Binary Search
[1,2,3,5] 3
[1,7] 1
kth-smallest-element-in-a-sorted-matrix)kth-smallest-number-in-multiplication-table)find-k-th-smallest-pair-distance)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #786: K-th Smallest Prime Fraction
class Solution {
public int[] kthSmallestPrimeFraction(int[] arr, int k) {
int n = arr.length;
Queue<Frac> pq = new PriorityQueue<>();
for (int i = 1; i < n; i++) {
pq.offer(new Frac(1, arr[i], 0, i));
}
for (int i = 1; i < k; i++) {
Frac f = pq.poll();
if (f.i + 1 < f.j) {
pq.offer(new Frac(arr[f.i + 1], arr[f.j], f.i + 1, f.j));
}
}
Frac f = pq.peek();
return new int[] {f.x, f.y};
}
static class Frac implements Comparable {
int x, y, i, j;
public Frac(int x, int y, int i, int j) {
this.x = x;
this.y = y;
this.i = i;
this.j = j;
}
@Override
public int compareTo(Object o) {
return x * ((Frac) o).y - ((Frac) o).x * y;
}
}
}
// Accepted solution for LeetCode #786: K-th Smallest Prime Fraction
type frac struct{ x, y, i, j int }
type hp []frac
func (a hp) Len() int { return len(a) }
func (a hp) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a hp) Less(i, j int) bool { return a[i].x*a[j].y < a[j].x*a[i].y }
func (a *hp) Push(x any) { *a = append(*a, x.(frac)) }
func (a *hp) Pop() any { l := len(*a); tmp := (*a)[l-1]; *a = (*a)[:l-1]; return tmp }
func kthSmallestPrimeFraction(arr []int, k int) []int {
n := len(arr)
h := make(hp, 0, n-1)
for i := 1; i < n; i++ {
h = append(h, frac{1, arr[i], 0, i})
}
heap.Init(&h)
for i := 1; i < k; i++ {
f := heap.Pop(&h).(frac)
if f.i+1 < f.j {
heap.Push(&h, frac{arr[f.i+1], arr[f.j], f.i + 1, f.j})
}
}
return []int{h[0].x, h[0].y}
}
# Accepted solution for LeetCode #786: K-th Smallest Prime Fraction
class Solution:
def kthSmallestPrimeFraction(self, arr: List[int], k: int) -> List[int]:
h = [(1 / y, 0, j + 1) for j, y in enumerate(arr[1:])]
heapify(h)
for _ in range(k - 1):
_, i, j = heappop(h)
if i + 1 < j:
heappush(h, (arr[i + 1] / arr[j], i + 1, j))
return [arr[h[0][1]], arr[h[0][2]]]
// Accepted solution for LeetCode #786: K-th Smallest Prime Fraction
struct Solution;
use std::cmp::Ord;
use std::cmp::Ordering;
use std::collections::BinaryHeap;
struct Fraction(i32, i32, usize, usize);
impl PartialEq for Fraction {
fn eq(&self, other: &Self) -> bool {
self.0 * other.1 == self.1 * other.0
}
}
impl Eq for Fraction {}
impl PartialOrd for Fraction {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Fraction {
fn cmp(&self, other: &Self) -> Ordering {
(self.1 * other.0).cmp(&(self.0 * other.1))
}
}
impl Solution {
fn kth_smallest_prime_fraction(a: Vec<i32>, k: i32) -> Vec<i32> {
let mut queue: BinaryHeap<Fraction> = BinaryHeap::new();
let n = a.len();
let k = k as usize;
for i in 0..n {
queue.push(Fraction(a[i], a[n - 1], i, n - 1));
}
for _ in 0..k - 1 {
let f = queue.pop().unwrap();
if f.3 - 1 > f.2 {
queue.push(Fraction(a[f.2], a[f.3 - 1], f.2, f.3 - 1));
}
}
let f = queue.pop().unwrap();
vec![f.0, f.1]
}
}
#[test]
fn test() {
let a = vec![1, 2, 3, 5];
let k = 3;
let res = vec![2, 5];
assert_eq!(Solution::kth_smallest_prime_fraction(a, k), res);
let a = vec![1, 7];
let k = 1;
let res = vec![1, 7];
assert_eq!(Solution::kth_smallest_prime_fraction(a, k), res);
}
// Accepted solution for LeetCode #786: K-th Smallest Prime Fraction
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #786: K-th Smallest Prime Fraction
// class Solution {
// public int[] kthSmallestPrimeFraction(int[] arr, int k) {
// int n = arr.length;
// Queue<Frac> pq = new PriorityQueue<>();
// for (int i = 1; i < n; i++) {
// pq.offer(new Frac(1, arr[i], 0, i));
// }
// for (int i = 1; i < k; i++) {
// Frac f = pq.poll();
// if (f.i + 1 < f.j) {
// pq.offer(new Frac(arr[f.i + 1], arr[f.j], f.i + 1, f.j));
// }
// }
// Frac f = pq.peek();
// return new int[] {f.x, f.y};
// }
//
// static class Frac implements Comparable {
// int x, y, i, j;
//
// public Frac(int x, int y, int i, int j) {
// this.x = x;
// this.y = y;
// this.i = i;
// this.j = j;
// }
//
// @Override
// public int compareTo(Object o) {
// return x * ((Frac) o).y - ((Frac) o).x * y;
// }
// }
// }
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair of elements. The outer loop picks one element, the inner loop scans the rest. For n elements that is n × (n−1)/2 comparisons = O(n²). No extra memory — just two loop variables.
Each pointer traverses the array at most once. With two pointers moving inward (or both moving right), the total number of steps is bounded by n. Each comparison is O(1), giving O(n) overall. No auxiliary data structures are needed — just two index variables.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Advancing both pointers shrinks the search space too aggressively and skips candidates.
Usually fails on: A valid pair can be skipped when only one side should move.
Fix: Move exactly one pointer per decision branch based on invariant.
Wrong move: Setting `lo = mid` or `hi = mid` can stall and create an infinite loop.
Usually fails on: Two-element ranges never converge.
Fix: Use `lo = mid + 1` or `hi = mid - 1` where appropriate.