K-th Element of Two Sorted Arrays
Problem and examples
Given two sorted arrays arr1 and arr2, and a number k, your task is to find the K-th smallest element in the merged array formed by combining both arrays.
- Both arrays are individually sorted in non-decreasing order.
- You are not allowed to fully merge the arrays or use extra space.
- Find the result using an efficient approach.
If either array is empty, the answer will depend entirely on the other array. If k is invalid (e.g., larger than total elements), return -1.
| Array 1 | Array 2 | k | Output | Description |
|---|---|---|---|---|
| [2, 3, 6, 7, 9] | [1, 4, 8, 10] | 5 | 4 | Combined sorted array: [1,2,3,4,6,...], 5th element is 4 |
| [1, 2, 3] | [4, 5, 6] | 4 | 4 | Combined: [1,2,3,4,5,6], 4th element is 4 |
| [1, 2] | [3, 4, 5, 6] | 6 | 6 | 6th smallest element overall |
| [1, 2, 3, 4] | [] | 2 | 2 | Only one array present, pick 2nd element |
| [] | [5, 10, 15] | 3 | 15 | Only one array present, pick 3rd element |
| [] | [5, 10, 15] | 4 | -1 | k = 4 is out of range (array size = 3) |
| [] | [] | 1 | -1 | Both arrays are empty |
| [1] | [2] | 2 | 2 | Combined: [1, 2], 2nd element is 2 |
Explore Find K-th Element of Two Sorted Arrays
Load an example or enter your own input. Use the player controls to follow each change.
Understanding the Problem
We are given two sorted arrays and a number k. Our goal is to find the k-th smallest element in the combined sorted form of these two arrays. But we are not supposed to merge them completely.
For a beginner, imagine placing all elements from both arrays into one big array and sorting it. Then, the k-th element is simply at index k - 1. But this is inefficient. So instead, we use a smart trick called binary search to figure out the answer without merging.
Step-by-Step Solution with Example
Step 1: Choose an Example
Let’s take arr1 = [2, 3, 6, 7, 9] and arr2 = [1, 4, 8, 10], and we want to find k = 5.
If we merge and sort: [1, 2, 3, 4, 6, 7, 8, 9, 10], the 5th smallest element is 6. We’ll now try to find 6 without merging.
Step 2: Always Binary Search the Smaller Array
We always do binary search on the smaller array to keep the process efficient. In our case, arr2 is smaller (4 elements), so we search there.
Step 3: Understand the Partition Logic
We divide arr2 such that we take cut2 elements from it and cut1 = k - cut2 from arr1.
We want all elements on the left side (from both arrays) to be less than or equal to the elements on the right side. We compare the largest on the left and the smallest on the right to check this.
Step 4: Perform Binary Search
We initialize low = max(0, k - len(arr1)) and high = min(k, len(arr2)).
We perform binary search between low and high, computing cut2 in the middle, and getting cut1 = k - cut2.
We extract four values:
l1 = -Infinityifcut1 == 0elsearr1[cut1 - 1]l2 = -Infinityifcut2 == 0elsearr2[cut2 - 1]r1 = Infinityifcut1 == len(arr1)elsearr1[cut1]r2 = Infinityifcut2 == len(arr2)elsearr2[cut2]
We check if l1 ≤ r2 and l2 ≤ r1. If true, the answer is max(l1, l2).
If l1 > r2, move left: high = cut2 - 1. If l2 > r1, move right: low = cut2 + 1.
Step 5: Return the Final Answer
When the correct partition is found, return max(l1, l2) as the k-th smallest element.
Edge Cases
Empty Array
If one array is empty, just return the k-th element from the other array directly.
Example: arr1 = [], arr2 = [5, 7, 9], k = 2 → Answer is 7.
K is Out of Range
If k > len(arr1) + len(arr2), it's invalid. We return -1 or a suitable error message.
All Elements are Same
Even if all elements are the same, the logic still works. The partitions will respect the counts, not the values.
Highly Unbalanced Lengths
Even if one array is very short and the other is long, binary search will happen on the smaller one, so performance remains efficient.
Finally
This approach avoids merging the arrays completely and instead finds the correct "cut" using binary search. This gives us a time complexity of O(log(min(n, m))), which is much faster than the naive approach.
By handling edge cases smartly and applying partitioning logic carefully, we can find the k-th smallest element in two sorted arrays in a time-efficient and space-efficient manner, perfect for both interviews and real-world problems.
- Let
arr1be the smaller array among the two. If not, swaparr1andarr2. - Let
n = arr1.lengthandm = arr2.length. - Set low = max(0, k - m) and high = min(k, n).
- While low ≤ high, do:
- → Set
cut1 = (low + high) / 2,cut2 = k - cut1. - → Use
l1 = -∞ifcut1 == 0, elsearr1[cut1 - 1]. - → Use
l2 = -∞ifcut2 == 0, elsearr2[cut2 - 1]. - → Use
r1 = ∞ifcut1 == n, elsearr1[cut1]. - → Use
r2 = ∞ifcut2 == m, elsearr2[cut2]. - If
l1 ≤ r2andl2 ≤ r1, returnmax(l1, l2). - Else if
l1 > r2, move left:high = cut1 - 1. - Else, move right:
low = cut1 + 1.
Implementation and efficiency
C implementation
#include <stdio.h>
#include <limits.h>
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }
int findKthElement(int* A, int* B, int n, int m, int k) {
if (n > m) return findKthElement(B, A, m, n, k);
int low = max(0, k - m), high = min(k, n);
while (low <= high) {
int cut1 = (low + high) / 2;
int cut2 = k - cut1;
int l1 = cut1 == 0 ? INT_MIN : A[cut1 - 1];
int l2 = cut2 == 0 ? INT_MIN : B[cut2 - 1];
int r1 = cut1 == n ? INT_MAX : A[cut1];
int r2 = cut2 == m ? INT_MAX : B[cut2];
if (l1 <= r2 && l2 <= r1) return max(l1, l2);
else if (l1 > r2) high = cut1 - 1;
else low = cut1 + 1;
}
return -1;
}
int main() {
int arr1[] = {2, 3, 6, 7, 9};
int arr2[] = {1, 4, 8, 10};
int k = 5;
printf("K-th element: %d\n", findKthElement(arr1, arr2, 5, 4, k));
return 0;
}C++ implementation
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int findKthElement(vector<int>& A, vector<int>& B, int k) {
int n = A.size(), m = B.size();
if (n > m) return findKthElement(B, A, k);
int low = max(0, k - m), high = min(k, n);
while (low <= high) {
int cut1 = (low + high) / 2;
int cut2 = k - cut1;
int l1 = (cut1 == 0) ? INT_MIN : A[cut1 - 1];
int l2 = (cut2 == 0) ? INT_MIN : B[cut2 - 1];
int r1 = (cut1 == n) ? INT_MAX : A[cut1];
int r2 = (cut2 == m) ? INT_MAX : B[cut2];
if (l1 <= r2 && l2 <= r1) return max(l1, l2);
else if (l1 > r2) high = cut1 - 1;
else low = cut1 + 1;
}
return -1;
}
int main() {
vector<int> arr1 = {2, 3, 6, 7, 9};
vector<int> arr2 = {1, 4, 8, 10};
int k = 5;
cout << "K-th element: " << findKthElement(arr1, arr2, k) << endl;
return 0;
}Python implementation
def find_kth_element(arr1, arr2, k):
n, m = len(arr1), len(arr2)
if n > m:
return find_kth_element(arr2, arr1, k)
low, high = max(0, k - m), min(k, n)
while low <= high:
cut1 = (low + high) // 2
cut2 = k - cut1
l1 = float('-inf') if cut1 == 0 else arr1[cut1 - 1]
l2 = float('-inf') if cut2 == 0 else arr2[cut2 - 1]
r1 = float('inf') if cut1 == n else arr1[cut1]
r2 = float('inf') if cut2 == m else arr2[cut2]
if l1 <= r2 and l2 <= r1:
return max(l1, l2)
elif l1 > r2:
high = cut1 - 1
else:
low = cut1 + 1
return -1
arr1 = [2, 3, 6, 7, 9]
arr2 = [1, 4, 8, 10]
k = 5
print("K-th element:", find_kth_element(arr1, arr2, k))Java implementation
public class KthElementFinder {
public static int findKthElement(int[] arr1, int[] arr2, int k) {
if (arr1.length > arr2.length) return findKthElement(arr2, arr1, k);
int n = arr1.length, m = arr2.length;
int low = Math.max(0, k - m), high = Math.min(k, n);
while (low <= high) {
int cut1 = (low + high) / 2;
int cut2 = k - cut1;
int l1 = (cut1 == 0) ? Integer.MIN_VALUE : arr1[cut1 - 1];
int l2 = (cut2 == 0) ? Integer.MIN_VALUE : arr2[cut2 - 1];
int r1 = (cut1 == n) ? Integer.MAX_VALUE : arr1[cut1];
int r2 = (cut2 == m) ? Integer.MAX_VALUE : arr2[cut2];
if (l1 <= r2 && l2 <= r1) return Math.max(l1, l2);
else if (l1 > r2) high = cut1 - 1;
else low = cut1 + 1;
}
return -1;
}
public static void main(String[] args) {
int[] arr1 = {2, 3, 6, 7, 9};
int[] arr2 = {1, 4, 8, 10};
int k = 5;
System.out.println("K-th element: " + findKthElement(arr1, arr2, k));
}
}JavaScript implementation
function findKthElement(arr1, arr2, k) {
if (arr1.length > arr2.length) return findKthElement(arr2, arr1, k);
let n = arr1.length, m = arr2.length;
let low = Math.max(0, k - m), high = Math.min(k, n);
while (low <= high) {
let cut1 = Math.floor((low + high) / 2);
let cut2 = k - cut1;
let l1 = cut1 === 0 ? -Infinity : arr1[cut1 - 1];
let l2 = cut2 === 0 ? -Infinity : arr2[cut2 - 1];
let r1 = cut1 === n ? Infinity : arr1[cut1];
let r2 = cut2 === m ? Infinity : arr2[cut2];
if (l1 <= r2 && l2 <= r1) return Math.max(l1, l2);
else if (l1 > r2) high = cut1 - 1;
else low = cut1 + 1;
}
return -1;
}
const arr1 = [2, 3, 6, 7, 9];
const arr2 = [1, 4, 8, 10];
const k = 5;
console.log("K-th element:", findKthElement(arr1, arr2, k));Go implementation
package main
import (
"fmt"
"math"
)
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func findKthElement(A, B []int, k int) int {
if len(A) > len(B) {
return findKthElement(B, A, k)
}
n, m := len(A), len(B)
low, high := max(0, k-m), min(k, n)
for low <= high {
cut1 := (low + high) / 2
cut2 := k - cut1
l1 := math.MinInt
if cut1 != 0 {
l1 = A[cut1-1]
}
l2 := math.MinInt
if cut2 != 0 {
l2 = B[cut2-1]
}
r1 := math.MaxInt
if cut1 != n {
r1 = A[cut1]
}
r2 := math.MaxInt
if cut2 != m {
r2 = B[cut2]
}
if l1 <= r2 && l2 <= r1 {
return max(l1, l2)
} else if l1 > r2 {
high = cut1 - 1
} else {
low = cut1 + 1
}
}
return -1
}
func main() {
arr1 := []int{2, 3, 6, 7, 9}
arr2 := []int{1, 4, 8, 10}
k := 5
fmt.Println("K-th element:", findKthElement(arr1, arr2, k))
}Rust implementation
fn find_kth_element(a: &[i32], b: &[i32], k: usize) -> i32 {
let (a, b) = if a.len() > b.len() { (b, a) } else { (a, b) };
let (n, m) = (a.len(), b.len());
let (mut low, mut high) = (k.saturating_sub(m), std::cmp::min(k, n));
while low <= high {
let cut1 = (low + high) / 2;
let cut2 = k - cut1;
let l1 = if cut1 == 0 { i32::MIN } else { a[cut1 - 1] };
let l2 = if cut2 == 0 { i32::MIN } else { b[cut2 - 1] };
let r1 = if cut1 == n { i32::MAX } else { a[cut1] };
let r2 = if cut2 == m { i32::MAX } else { b[cut2] };
if l1 <= r2 && l2 <= r1 {
return l1.max(l2);
} else if l1 > r2 {
high = cut1 - 1;
} else {
low = cut1 + 1;
}
}
-1
}
fn main() {
let arr1 = vec![2, 3, 6, 7, 9];
let arr2 = vec![1, 4, 8, 10];
let k = 5;
println!("K-th element: {}", find_kth_element(&arr1, &arr2, k));
}Kotlin implementation
fun findKthElement(arr1: IntArray, arr2: IntArray, k: Int): Int {
if (arr1.size > arr2.size) return findKthElement(arr2, arr1, k)
var low = maxOf(0, k - arr2.size)
var high = minOf(k, arr1.size)
while (low <= high) {
val cut1 = (low + high) / 2
val cut2 = k - cut1
val l1 = if (cut1 == 0) Int.MIN_VALUE else arr1[cut1 - 1]
val l2 = if (cut2 == 0) Int.MIN_VALUE else arr2[cut2 - 1]
val r1 = if (cut1 == arr1.size) Int.MAX_VALUE else arr1[cut1]
val r2 = if (cut2 == arr2.size) Int.MAX_VALUE else arr2[cut2]
if (l1 <= r2 && l2 <= r1) return maxOf(l1, l2)
else if (l1 > r2) high = cut1 - 1
else low = cut1 + 1
}
return -1
}
fun main() {
val arr1 = intArrayOf(2, 3, 6, 7, 9)
val arr2 = intArrayOf(1, 4, 8, 10)
val k = 5
println("K-th element: ${findKthElement(arr1, arr2, k)}")
}Swift implementation
func findKthElement(_ arr1: [Int], _ arr2: [Int], _ k: Int) -> Int {
var A = arr1, B = arr2
if A.count > B.count {
swap(&A, &B)
}
let n = A.count, m = B.count
var low = max(0, k - m), high = min(k, n)
while low <= high {
let cut1 = (low + high) / 2
let cut2 = k - cut1
let l1 = (cut1 == 0) ? Int.min : A[cut1 - 1]
let l2 = (cut2 == 0) ? Int.min : B[cut2 - 1]
let r1 = (cut1 == n) ? Int.max : A[cut1]
let r2 = (cut2 == m) ? Int.max : B[cut2]
if l1 <= r2 && l2 <= r1 {
return max(l1, l2)
} else if l1 > r2 {
high = cut1 - 1
} else {
low = cut1 + 1
}
}
return -1
}
let arr1 = [2, 3, 6, 7, 9]
let arr2 = [1, 4, 8, 10]
let k = 5
print("K-th element: \(findKthElement(arr1, arr2, k))")TypeScript implementation
function findKthElement(arr1: number[], arr2: number[], k: number): number {
if (arr1.length > arr2.length) return findKthElement(arr2, arr1, k);
let n = arr1.length, m = arr2.length;
let low = Math.max(0, k - m), high = Math.min(k, n);
while (low <= high) {
const cut1 = Math.floor((low + high) / 2);
const cut2 = k - cut1;
const l1 = cut1 === 0 ? -Infinity : arr1[cut1 - 1];
const l2 = cut2 === 0 ? -Infinity : arr2[cut2 - 1];
const r1 = cut1 === n ? Infinity : arr1[cut1];
const r2 = cut2 === m ? Infinity : arr2[cut2];
if (l1 <= r2 && l2 <= r1) return Math.max(l1, l2);
else if (l1 > r2) high = cut1 - 1;
else low = cut1 + 1;
}
return -1;
}
const arr1: number[] = [2, 3, 6, 7, 9];
const arr2: number[] = [1, 4, 8, 10];
const k = 5;
console.log("K-th element:", findKthElement(arr1, arr2, k));Time complexity
| Case | Time | Explanation |
|---|---|---|
| best case | O(1) | If the correct partition is found in the first attempt. |
| average case | O(log(min(n, m))) | Binary search runs on the smaller array, halving the search space at each step. |
| worst case | O(log(min(n, m))) | The binary search continues until all partition points are exhausted on the smaller array. |
Space complexity
O(1)
Only a constant amount of additional variables are used for computation and control.