Open-Source Internship opportunity by OpenGenus for programmers. Apply now.
In this article, we have discussed how can we find the kth smallest element in a given n x n matrix, in which every row and column is sorted in non-decreasing order.
Table of contents:
- Problem Statement
- Brute force approach
- Using min-heap data structure
- Using binary search
Problem statement
Problem statement: Find the kth smallest element in a given n x n matrix, in which every row and column is sorted in non-decreasing order.
Example:
- K=3, matrix:
15, 25, 31, 43
16, 27, 36, 45
28, 30, 39, 49
33, 35, 40, 50
Output: 25
Explanation: The 3rd smallest element = 25
- K=7, matrix:
15, 18, 33
17, 29, 37
25, 31, 38
Output: 33
Explanation: The 7th smallest element is 33
We will solve this using three approaches:
- Brute force approach
- Using min-heap data structure
- Using binary search
Brute force approach
In this approach we simply add all the elements of the matrix into an array, sort the array and return the k-th smallest number.
Algorithm:
- If given a matrix mat[][] of dimensions n x n, we create a linear array arr[] of size n*n.
- Now we add all the elements of mat into the arr.
- We then sort arr and return arr[k-1] (k-th largest element).
Implementation:
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
int kthSmallestElement(vector < vector<int> > matrix, int k)
{
int n = matrix.size();
if(k > n*n)
{
return -1;
}
if(k == 1)
{
return matrix[0][0];
}
vector <int> a;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
a.push_back(matrix[i][j]);
}
}
sort(a.begin(),a.end());
return a[k-1];
}
int main()
{
vector< vector<int> > matrix {
{15, 25, 31, 43},
{16, 27, 36, 45},
{28, 30, 39, 49},
{33, 35, 40, 50},
};
int k = 3;
int ans = kthSmallestElement(matrix,k);
if(ans == -1){
cout<<k<<" smallest element doesn't exist.";
}
else{
cout<<k<<" smallest element is "<<ans<<endl;
}
return 0;
}
Complexity Analysis:
Time Complexity : T(n) = O(n^2 log(n^2))
Storing the matrix in an array requires O(n^2)time, Sorting the array requires O(n^2 log(n^2)), Taking the K th element takes O(1) time.
So the total time complexity is O(n^2 log(n^2)).
Space Complexity : O(n^2)
As we require an auxiliary of size n^2.
Using min-heap data structure
Here, the idea is to use min heap data structure to store the elements of the first row of the matrix, then one by one w pop elements from the heap and add the element from the matrix lying just below the last popped element. This is done until we obtain k-th smallest number.
Algorithm:
- Create a Min-Heap to store the elements such that, a heap entry also stores row number and column number.
- Traverse the first row and build a min heap of elements from first row.
- Run a loop k times to extract min element from heap and in each iteration do the following.
- Get minimum element that is the root from min heap.
- Find row number and column number of this element.
- Replace root with the next element from same column (element from the matrix lying just below the last popped element) and min-heapify the root.
- Lastly, print the kth minimum element, which is the last extracted element .
Implementation:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
struct element
{
int val;
int i;
int j;
element(){}
element(int element,int row,int col)
{
val = element;
i = row;
j = col;
}
};
struct compare
{
bool operator()(element const& p1, element const& p2)
{
return p1.val > p2.val;
}
};
int kthSmallestElement(vector < vector<int> > matrix,int k)
{
int n = matrix.size();
if(k > n*n)
return -1;
if(k == 1)
return matrix[0][0];
priority_queue <struct element,vector <element>,compare > minheap;
for(int i=0;i<n;i++)
{
struct element current(matrix[0][i],0,i);
minheap.push(current);
}
struct element kthsmall;
for(int i=0;i<k;i++)
{
kthsmall = minheap.top();
minheap.pop();
if(kthsmall.i+1 >= n)
{
struct element current(INT_MAX,-1,-1);
minheap.push(current);
}
else
{
struct element current(matrix[kthsmall.i + 1][kthsmall.j],kthsmall.i+1,kthsmall.j);
minheap.push(current);
}
}
return kthsmall.val;
}
int main()
{
vector< vector<int> > matrix {
{15, 25, 31, 43},
{16, 27, 36, 45},
{28, 30, 39, 49},
{33, 35, 40, 50},
};
int k = 3;
int ans = kthSmallestElement(matrix,k);
if(ans == -1){
cout<<k<<" smallest element doesn't exist.";
}
else{
cout<<k<<" smallest element is "<<ans<<endl;
}
return 0;
}
Complexity Analysis:
Time Complexity: O(k logn)
Building a min-heap takes O(n) time and as heapify takes O(log n) time therefore k times heapify takes O(k logn) time.
Space Complexity: O(n),
Min-Heap stores one row at a time so the space complexity is O(n).
Using binary search
In this approach we use binary search to iterate over possible solutions. We know that answer >= mat[0][0] and also answer <= mat[N-1][N-1]. So mat[0][0] and mat[N-1][N-1] can represent the “range” i.e., the low and the high values for the Binary Search.
Algorithm:
- We start the Binary Search with low = mat[0][0] and high = mat[n-1][n-1].
- We then find mid of low and high. But this middle number is may not necessarily be an element in the matrix.
- We count all the numbers smaller than or equal to mid element in the matrix. We can do this in O(N) as the matrix is sorted.
- If this count is less than ‘K’, we update low = mid+1 and search in higher part of the matrix.
- Else if the count is greater than or equal to ‘K’, we update high = mid and search in the lower part of the matrix.
- When value of low and high become equal and the loop terminates, low contains the value of k-th smallest element.
Example:
matrix =
15, 25, 31, 43
16, 27, 36, 45
28, 30, 39, 49
33, 35, 40, 50
low=15 and high=50
Steps:
low high mid count
15 50 32 7 (count is not lesser than 3 ∴ high = mid)
15 32 23 2 (count is lesser than 3 ∴ low = mid + 1)
24 32 28 5 (count is not lesser than 3 ∴ high = mid)
24 28 26 3 (count is not lesser than 3 ∴ high = mid)
24 26 25 3 (count is not lesser than 3 ∴ high = mid)
24 25 24 2 (count is lesser than 3 ∴ low = mid + 1)
25 25 (low is not lesser than mid, loop terminates)
Implementation:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int kthSmallestElement(vector < vector<int> > matrix,int k)
{
int n = matrix.size();
if(k == 0 || k > n*n || n == 0)
return -1;
if(k == 1)
return matrix[0][0];
int low = matrix[0][0];
int high = matrix[n-1][n-1];
while (low < high)
{
int mid = low + (high - low) / 2;
int count = 0;
int j = n - 1;
for (int i = 0; i < n; i++)
{
while (j >= 0 && matrix[i][j] > mid)
j--;
count += (j + 1);
}
if (count < k)
low = mid + 1;
else
high = mid;
}
return low;
}
int main()
{
vector< vector<int> > matrix {
{15, 25, 31, 43},
{16, 27, 36, 45},
{28, 30, 39, 49},
{33, 35, 40, 50},
};
int k = 3;
int ans = kthSmallestElement(matrix,k);
if(ans == -1){
cout<<k<<" smallest element doesn't exist.";
}
else{
cout<<k<<" smallest element is "<<ans<<endl;
}
return 0;
}
Complexity Analysis
Time Complexity : O(n)
O(n log(max-min)) = O(n log(const)) = O(n)
max and min both are integers as max-min is the difference between largest and smallest values in the matrix so the maximum difference can’t be more than maximum integer value and hence the difference max-min becomes constant.
Space Complexity : O(1)
So by the end of this article you must have a clear understanding of how to find the kth smallest element in a row and column wise sorted matrix.
With this article, you must have a strong knowledge to find the