668. Kth Smallest Number in Multiplication Table
Problem Description
You're given a multiplication table of size m x n, where each cell at position [i][j] contains the product i * j (using 1-based indexing). For example, a 3x3 multiplication table would look like:
1 2 3 2 4 6 3 6 9
The task is to find the kth smallest element when all values in this multiplication table are considered in sorted order.
Input:
m: number of rows in the multiplication tablen: number of columns in the multiplication tablek: the position of the element to find (1-indexed)
Output:
- The
kth smallest value in the multiplication table
Example:
For a 3x3 table with k = 5, the values in sorted order would be: [1, 2, 2, 3, 3, 4, 6, 6, 9]. The 5th smallest element is 3.
The solution uses binary search on the value range [1, m*n]. For each candidate value mid, it counts how many elements in the multiplication table are less than or equal to mid. This is done by iterating through each row i and calculating min(mid // i, n), which gives the count of elements in row i that are ≤ mid. If the total count is at least k, the answer is at most mid; otherwise, it must be larger than mid. The binary search continues until it converges on the exact kth smallest value.
Intuition
The key insight is that we don't need to generate the entire multiplication table to find the kth smallest element. Instead, we can use binary search on the possible values.
Think about it this way: the smallest possible value in the table is 1 (at position [1][1]), and the largest is m * n (at position [m][n]). The answer must be somewhere in this range.
For any given value x, we can efficiently count how many elements in the multiplication table are less than or equal to x without generating the table. How? For each row i, the elements are i, 2i, 3i, ..., n*i. The number of elements ≤ x in row i is simply min(x // i, n) - we divide x by i to see how many multiples of i are ≤ x, but cap it at n since each row has only n elements.
This counting ability enables a binary search strategy:
- If we pick a value
midand find that there are at leastkelements ≤mid, then our answer is at mostmid - If there are fewer than
kelements ≤mid, then our answer must be greater thanmid
The beauty of this approach is that binary search will naturally converge to a value that actually exists in the table. Even though we're searching through all integers in the range [1, m*n], the final answer will always be a valid product that appears in the multiplication table because we're looking for the smallest value where the count reaches k.
Learn more about Math and Binary Search patterns.
Solution Implementation
1class Solution:
2 def findKthNumber(self, m: int, n: int, k: int) -> int:
3 """
4 Find the kth smallest number in a multiplication table of size m x n.
5 Uses binary search template with first_true_index.
6 Feasible condition: count of elements <= mid is >= k
7 """
8 left, right = 1, m * n
9 first_true_index = -1
10
11 while left <= right:
12 mid = (left + right) // 2
13
14 # Count how many numbers in the multiplication table are <= mid
15 count = 0
16 for row in range(1, m + 1):
17 # For each row i, elements are: i, 2i, 3i, ..., n*i
18 # Number of elements <= mid in row i is min(mid // i, n)
19 count += min(mid // row, n)
20
21 # Feasible: count >= k means the kth number is at most mid
22 if count >= k:
23 first_true_index = mid
24 right = mid - 1
25 else:
26 left = mid + 1
27
28 return first_true_index
291class Solution {
2 /**
3 * Finds the kth smallest number in an m x n multiplication table.
4 * Uses binary search template with firstTrueIndex.
5 */
6 public int findKthNumber(int m, int n, int k) {
7 int left = 1;
8 int right = m * n;
9 int firstTrueIndex = -1;
10
11 while (left <= right) {
12 int mid = left + (right - left) / 2;
13
14 // Count how many numbers in the multiplication table are <= mid
15 int count = 0;
16 for (int row = 1; row <= m; row++) {
17 count += Math.min(mid / row, n);
18 }
19
20 // Feasible: count >= k means the kth number is at most mid
21 if (count >= k) {
22 firstTrueIndex = mid;
23 right = mid - 1;
24 } else {
25 left = mid + 1;
26 }
27 }
28
29 return firstTrueIndex;
30 }
31}
321class Solution {
2public:
3 int findKthNumber(int m, int n, int k) {
4 int left = 1;
5 int right = m * n;
6 int firstTrueIndex = -1;
7
8 while (left <= right) {
9 int mid = left + (right - left) / 2;
10
11 // Count how many numbers in the multiplication table are <= mid
12 int count = 0;
13 for (int row = 1; row <= m; ++row) {
14 count += std::min(mid / row, n);
15 }
16
17 // Feasible: count >= k means the kth number is at most mid
18 if (count >= k) {
19 firstTrueIndex = mid;
20 right = mid - 1;
21 } else {
22 left = mid + 1;
23 }
24 }
25
26 return firstTrueIndex;
27 }
28};
291function findKthNumber(m: number, n: number, k: number): number {
2 let left = 1;
3 let right = m * n;
4 let firstTrueIndex = -1;
5
6 while (left <= right) {
7 const mid = Math.floor((left + right) / 2);
8
9 // Count how many numbers in the multiplication table are <= mid
10 let count = 0;
11 for (let row = 1; row <= m; row++) {
12 count += Math.min(Math.floor(mid / row), n);
13 }
14
15 // Feasible: count >= k means the kth number is at most mid
16 if (count >= k) {
17 firstTrueIndex = mid;
18 right = mid - 1;
19 } else {
20 left = mid + 1;
21 }
22 }
23
24 return firstTrueIndex;
25}
26Solution Approach
We use the standard binary search template with first_true_index to find the kth smallest element.
Binary Search Setup:
left = 1: minimum possible value in the tableright = m * n: maximum possible value in the tablefirst_true_index = -1: tracks the first value where count >= k
Feasible Condition:
For any value mid, we count how many elements in the multiplication table are ≤ mid. If this count is >= k, then mid could be our answer (or the answer is smaller). This creates the monotonic pattern: false, false, ..., true, true, true.
Counting Elements ≤ mid:
For each row i (from 1 to m), the elements are i, 2i, 3i, ..., n*i. The count of elements ≤ mid in row i is min(mid // i, n):
mid // igives how many multiples ofiare ≤mid- We cap at
nbecause each row has at mostnelements
Binary Search Template:
while left <= right:
mid = (left + right) // 2
count = sum(min(mid // row, n) for row in range(1, m + 1))
if count >= k: # Feasible: kth element is at most mid
first_true_index = mid
right = mid - 1
else:
left = mid + 1
return first_true_index
Why the Result Exists in the Table: The binary search finds the smallest value where count >= k. This value is guaranteed to exist in the multiplication table because:
- If
midis in the table and count(mid) >= k while count(mid-1) < k, thenmidis exactly the kth smallest - The template converges to this exact boundary
The time complexity is O(m * log(m * n)) - O(log(m * n)) iterations with O(m) counting each. Space complexity is O(1).
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through the binary search template with m = 3, n = 3, k = 5.
Setup:
- Multiplication table:
1 2 3 2 4 6 3 6 9
- Sorted values: [1, 2, 2, 3, 3, 4, 6, 6, 9]
- Looking for 5th element (should be 3)
Initial state:
left = 1,right = 9,first_true_index = -1- Feasible condition: count of elements ≤ mid is >= 5
Iteration 1:
mid = (1 + 9) // 2 = 5- Count elements ≤ 5:
- Row 1: min(5/1, 3) = 3 elements (1, 2, 3)
- Row 2: min(5/2, 3) = 2 elements (2, 4)
- Row 3: min(5/3, 3) = 1 element (3)
- Total = 6
- Is 6 >= 5? Yes → Feasible
- Update:
first_true_index = 5,right = 4
Iteration 2:
left = 1,right = 4mid = (1 + 4) // 2 = 2- Count elements ≤ 2:
- Row 1: min(2/1, 3) = 2 elements (1, 2)
- Row 2: min(2/2, 3) = 1 element (2)
- Row 3: min(2/3, 3) = 0 elements
- Total = 3
- Is 3 >= 5? No → Not feasible
- Update:
left = 3
Iteration 3:
left = 3,right = 4mid = (3 + 4) // 2 = 3- Count elements ≤ 3:
- Row 1: min(3/1, 3) = 3 elements
- Row 2: min(3/2, 3) = 1 element
- Row 3: min(3/3, 3) = 1 element
- Total = 5
- Is 5 >= 5? Yes → Feasible
- Update:
first_true_index = 3,right = 2
Iteration 4:
left = 3,right = 2- Loop ends:
left > right
Result:
first_true_index = 3- The 5th smallest element is 3
The template correctly finds the boundary where count first reaches k.
Time and Space Complexity
Time Complexity: O(m * log(m * n))
The binary search runs from 1 to m * n, which means the search space has size m * n. Binary search takes O(log(m * n)) iterations to converge.
For each iteration of the binary search, we need to count how many elements in the multiplication table are less than or equal to mid. This counting process involves a loop that runs m times (from 1 to m), and for each iteration i, we calculate min(mid // i, n) which takes O(1) time.
Therefore, the total time complexity is O(m * log(m * n)).
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space for variables left, right, mid, cnt, and the loop variable i. No additional data structures that scale with input size are used.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template Variant
The Pitfall: A common mistake is using while left < right with right = mid instead of the standard template with while left <= right and right = mid - 1.
Incorrect (non-standard template):
while left < right: mid = (left + right) // 2 if count >= k: right = mid else: left = mid + 1 return left
Correct (standard template):
first_true_index = -1 while left <= right: mid = (left + right) // 2 if count >= k: first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index
The standard template with first_true_index clearly tracks the answer and handles edge cases consistently.
2. Incorrect Counting Logic
The Pitfall: Not considering the table boundaries when counting:
Incorrect:
count += mid // row # Missing the min() with n
Why This Fails:
Each row has exactly n elements. Without min(mid // row, n), we count more elements than exist. If mid = 100, row = 1, and n = 5, we'd count 100 elements instead of 5.
Correct:
count += min(mid // row, n)
3. Using 0-Based Indexing for Rows
Incorrect:
for row in range(m): # Wrong: starts from 0
count += min(mid // (row + 1), n)
Why This Fails: The multiplication table uses 1-based indexing. Starting from row 0 causes division by zero or incorrect calculations.
Correct:
for row in range(1, m + 1): # Start from 1, go up to m inclusive
count += min(mid // row, n)
4. Integer Overflow in Large Inputs
In languages with fixed integer sizes, calculating left + right can overflow.
Safe Approach:
mid = left + (right - left) // 2 # Prevents overflow
Which of the following is a min heap? 
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!