K’th Smallest/Largest Element in Unsorted Array | Expected Linear Time
Given an array of distinct integers and an integer k
, where k
is smaller than the array’s size, the task is to find the k’th smallest element in the array.
Examples:
Input:
arr = [7, 10, 4, 3, 20, 15]
,k = 3
Output:7
Explanation: The sorted array is[3, 4, 7, 10, 15, 20]
, so the 3rd smallest element is7
.Input:
arr = [7, 10, 4, 3, 20, 15]
,k = 4
Output:10
Explanation: The sorted array is[3, 4, 7, 10, 15, 20]
, so the 4th smallest element is10
.
Please note that there are multiple ways to solve this problem discussed in kth-Smallest/Largest Element in Unsorted Array. The solution discussed here works best in practice.
The idea is to use a randomized pivot selection to partition the array, reducing the search space by focusing on the subarray where the k’th element must lie.
Step by step approach:
Choose a Random Pivot: Randomly select an element as the pivot. This helps avoid the worst-case scenario in some cases (like when the array is already sorted).
Partitioning: Rearrange the array such that all elements less than the pivot are on the left side, and those greater than the pivot are on the right side.
Recursive Search: Once the pivot is positioned, if its index equals n-k
comparison , then it’s the Kth largest element. If not, recursively search the appropriate partition (left or right) based on the with n-k
.
// C++ program to find K’th Smallest/
// Largest Element in Unsorted Array
#include<bits/stdc++.h>
using namespace std;
// Partition function: Rearranges elements
// around a pivot (last element)
int partition(vector<int> &arr, int l, int r) {
int x = arr[r];
int i = l;
// Iterate through the subarray
for (int j = l; j <= r - 1; j++) {
// Move elements <= pivot to the
// left partition
if (arr[j] <= x) {
swap(arr[i], arr[j]);
i++;
}
}
// Place the pivot in its correct position
swap(arr[i], arr[r]);
return i;
}
// Randomizes the pivot to avoid worst-case performance
int randomPartition(vector<int> &arr, int l, int r) {
int n = r - l + 1;
int pivot = rand() % n;
swap(arr[l + pivot], arr[r]);
return partition(arr, l, r);
}
// function to find the k'th smallest element
// using QuickSelect
int quickSelect(vector<int> &arr, int l, int r, int k) {
// Check if k is within the valid range
// of the current subarray
if (k > 0 && k <= r - l + 1) {
// Partition the array and get the
// pivot's final position
int pos = randomPartition(arr, l, r);
// If pivot is the k'th element, return it
if (pos - l == k - 1)
return arr[pos];
// If pivot's position is larger than k,
// search left subarray
if (pos - l > k - 1)
return quickSelect(arr, l, pos - 1, k);
// Otherwise, search right subarray and adjust k
// (k is reduced by the size of the left partition)
return quickSelect(arr, pos + 1, r, k - (pos - l + 1));
}
// Return infinity for invalid k (error handling)
return INT_MAX;
}
int kthSmallest(vector<int> &arr, int k) {
int n = arr.size();
return quickSelect(arr, 0, n-1, k);
}
int main() {
vector<int> arr = {12, 3, 5, 7, 4, 19, 26};
int k = 3;
cout << kthSmallest(arr, k);
return 0;
}
// Java program to find K’th Smallest/
// Largest Element in Unsorted Array
import java.util.Random;
class GfG {
// Partition function: Rearranges elements
// around a pivot (last element)
static int partition(int[] arr, int l, int r) {
int x = arr[r];
int i = l;
// Iterate through the subarray
for (int j = l; j <= r - 1; j++) {
// Move elements <= pivot to the left partition
if (arr[j] <= x) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
}
}
// Place the pivot in its correct position
int temp = arr[i];
arr[i] = arr[r];
arr[r] = temp;
return i;
}
// Randomizes the pivot to avoid worst-case performance
static int randomPartition(int[] arr, int l, int r) {
Random rand = new Random();
int n = r - l + 1;
int pivot = rand.nextInt(n);
int temp = arr[l + pivot];
arr[l + pivot] = arr[r];
arr[r] = temp;
return partition(arr, l, r);
}
// function to find the k'th smallest element using QuickSelect
static int quickSelect(int[] arr, int l, int r, int k) {
// Check if k is within the valid range of
// the current subarray
if (k > 0 && k <= r - l + 1) {
// Partition the array and get the
// pivot's final position
int pos = randomPartition(arr, l, r);
// If pivot is the k'th element, return it
if (pos - l == k - 1)
return arr[pos];
// If pivot's position is larger than k,
// search left subarray
if (pos - l > k - 1)
return quickSelect(arr, l, pos - 1, k);
// Otherwise, search right subarray and adjust k
// (k is reduced by the size of the left partition)
return quickSelect(arr, pos + 1, r, k - (pos - l + 1));
}
// Return infinity for invalid k (error handling)
return Integer.MAX_VALUE;
}
static int kthSmallest(int[] arr, int k) {
int n = arr.length;
return quickSelect(arr, 0, n - 1, k);
}
public static void main(String[] args) {
int[] arr = {12, 3, 5, 7, 4, 19, 26};
int k = 3;
System.out.println(kthSmallest(arr, k));
}
}
# Python program to find K’th Smallest/
# Largest Element in Unsorted Array
import random
# Partition function: Rearranges elements
# around a pivot (last element)
def partition(arr, l, r):
x = arr[r]
i = l
# Iterate through the subarray
for j in range(l, r):
# Move elements <= pivot to the left partition
if arr[j] <= x:
arr[i], arr[j] = arr[j], arr[i]
i += 1
# Place the pivot in its correct position
arr[i], arr[r] = arr[r], arr[i]
return i
# Randomizes the pivot to avoid worst-case performance
def randomPartition(arr, l, r):
n = r - l + 1
pivot = random.randint(0, n - 1)
arr[l + pivot], arr[r] = arr[r], arr[l + pivot]
return partition(arr, l, r)
# function to find the k'th smallest element using QuickSelect
def quickSelect(arr, l, r, k):
# Check if k is within the valid range of the current subarray
if 0 < k <= r - l + 1:
# Partition the array and get the pivot's final position
pos = randomPartition(arr, l, r)
# If pivot is the k'th element, return it
if pos - l == k - 1:
return arr[pos]
# If pivot's position is larger than k, search left subarray
if pos - l > k - 1:
return quickSelect(arr, l, pos - 1, k)
# Otherwise, search right subarray and adjust k
# (k is reduced by the size of the left partition)
return quickSelect(arr, pos + 1, r, k - (pos - l + 1))
# Return infinity for invalid k (error handling)
return float('inf')
def kthSmallest(arr, k):
n = len(arr)
return quickSelect(arr, 0, n - 1, k)
if __name__ == "__main__":
arr = [12, 3, 5, 7, 4, 19, 26]
k = 3
print(kthSmallest(arr, k))
// C# program to find K’th Smallest/
// Largest Element in Unsorted Array
using System;
class GfG {
// Partition function: Rearranges elements
// around a pivot (last element)
static int partition(int[] arr, int l, int r) {
int x = arr[r];
int i = l;
// Iterate through the subarray
for (int j = l; j <= r - 1; j++) {
// Move elements <= pivot to the left partition
if (arr[j] <= x) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
}
}
// Place the pivot in its correct position
int temp2 = arr[i];
arr[i] = arr[r];
arr[r] = temp2;
return i;
}
// Randomizes the pivot to avoid worst-case performance
static int randomPartition(int[] arr, int l, int r) {
Random rand = new Random();
int n = r - l + 1;
int pivot = rand.Next(n);
int temp = arr[l + pivot];
arr[l + pivot] = arr[r];
arr[r] = temp;
return partition(arr, l, r);
}
// function to find the k'th smallest element using QuickSelect
static int quickSelect(int[] arr, int l, int r, int k) {
// Check if k is within the valid range of the
// current subarray
if (k > 0 && k <= r - l + 1) {
// Partition the array and get the pivot's
// final position
int pos = randomPartition(arr, l, r);
// If pivot is the k'th element, return it
if (pos - l == k - 1)
return arr[pos];
// If pivot's position is larger than k, search
// left subarray
if (pos - l > k - 1)
return quickSelect(arr, l, pos - 1, k);
// Otherwise, search right subarray and adjust k
// (k is reduced by the size of the left partition)
return quickSelect(arr, pos + 1, r, k - (pos - l + 1));
}
// Return infinity for invalid k (error handling)
return int.MaxValue;
}
static int kthSmallest(int[] arr, int k) {
int n = arr.Length;
return quickSelect(arr, 0, n - 1, k);
}
static void Main() {
int[] arr = {12, 3, 5, 7, 4, 19, 26};
int k = 3;
Console.WriteLine(kthSmallest(arr, k));
}
}
// JavaScript program to find K’th Smallest/
// Largest Element in Unsorted Array
// Partition function: Rearranges elements
// around a pivot (last element)
function partition(arr, l, r) {
let x = arr[r];
let i = l;
// Iterate through the subarray
for (let j = l; j <= r - 1; j++) {
// Move elements <= pivot to the left partition
if (arr[j] <= x) {
[arr[i], arr[j]] = [arr[j], arr[i]];
i++;
}
}
// Place the pivot in its correct position
[arr[i], arr[r]] = [arr[r], arr[i]];
return i;
}
// Randomizes the pivot to avoid worst-case performance
function randomPartition(arr, l, r) {
let n = r - l + 1;
let pivot = Math.floor(Math.random() * n);
[arr[l + pivot], arr[r]] = [arr[r], arr[l + pivot]];
return partition(arr, l, r);
}
// function to find the k'th smallest element using QuickSelect
function quickSelect(arr, l, r, k) {
// Check if k is within the valid range of the current subarray
if (k > 0 && k <= r - l + 1) {
// Partition the array and get the pivot's final position
let pos = randomPartition(arr, l, r);
// If pivot is the k'th element, return it
if (pos - l == k - 1)
return arr[pos];
// If pivot's position is larger than k, search left subarray
if (pos - l > k - 1)
return quickSelect(arr, l, pos - 1, k);
// Otherwise, search right subarray and adjust k
// (k is reduced by the size of the left partition)
return quickSelect(arr, pos + 1, r, k - (pos - l + 1));
}
// Return Infinity for invalid k (error handling)
return Infinity;
}
function kthSmallest(arr, k) {
let n = arr.length;
return quickSelect(arr, 0, n - 1, k);
}
let arr = [12, 3, 5, 7, 4, 19, 26];
let k = 3;
console.log(kthSmallest(arr, k));
Output
5
Time Complexity: O(n) The worst-case time complexity of the above solution is still O(n2). In the worst case, the randomized function may always pick a corner element. However, the average-case time complexity is O(n)
. The assumption in the analysis is, random number generator is equally likely to generate any number in the input range.
Auxiliary Space: O(1) since using constant variables.
Even if the worst case time complexity is quadratic, this solution works best in practice.