Smallest subarray with k distinct numbers
Last Updated :
22 Jul, 2022
We are given an array consisting of n integers and an integer k. We need to find the minimum range in array [l, r] (both l and r are inclusive) such that there are exactly k different numbers. If such subarray doesn’t exist print “Invalid k”.
Examples:
Input : arr[] = { 1, 1, 2, 2, 3, 3, 4, 5}
k = 3
Output : 5 7
Input : arr[] = { 1, 2, 2, 3}
k = 2
Output : 0 1
Input : arr[] = {1, 1, 2, 1, 2}
k = 3
Output : Invalid k
Approach 1 : (Brute Force Method)
The simplest approach in this problem is, try to generate all the subarrays and check for which subarray the size is k. But there are some points we need to take care.
Steps:
- Pick each of the elements from the given array as the starting element [ i-th element ] of our required subarray.
- In each iteration initialize an empty set to store the distinct elements of the subarray
- Pick each remaining element [ i, i+1,..n – 1] from the array as the last element [ j-th element ].
- Add the current element to the set.
- If the set size equals k then update the results and break from the inner loop (already found k distinct elements increasing the size of the subarray has 2 possibilities either will get more distinct elements, or increase the subarray size with repeated elements which are not to be considered in the required results).
- If (j == n) or j = size of the array, i.e. we have not found any desired subarray starting from i-th index and going forward we will be having fewer elements to consider.
( For example : consider given array is 4 5 5 4 5 and k = 3, when start from 0th index we will not find any subarray of k size and j will reach end so that means we won’t get any element that can make a k = 3 size required subarray).
So, Break from the outer loop.
- Print the output if found, otherwise, print “Invalid k”.
Implementation:
C++
#include <bits/stdc++.h>
using namespace std;
void minRange( int arr[], int n, int k)
{
int start = 0, end = n;
for ( int i = 0; i < n; i++) {
unordered_set< int > set;
int j;
for (j = i; j < n; j++) {
set.insert(arr[j]);
if (set.size() == k) {
if (j - i < end - start) {
start = i;
end = j;
}
break ;
}
}
if (j == n) {
break ;
}
}
if (start == 0 && end == n)
cout << "Invalid k" ;
else
cout << start << " " << end;
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
int n = sizeof (arr) / sizeof (arr[0]);
int k = 3;
minRange(arr, n, k);
return 0;
}
|
Java
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
class GFG {
static void minRange( int arr[], int n, int k)
{
int start = 0 ;
int end = n;
for ( int i = 0 ; i < n; i++) {
HashSet<Integer> set = new HashSet<Integer>();
int j;
for (j = i; j < n; j++) {
set.add(arr[j]);
if (set.size() == k)
{
if (j - i < end - start) {
start = i;
end = j;
}
break ;
}
}
if (j == n)
break ;
}
if (start == 0 && end == n)
System.out.println( "Invalid k" );
else
System.out.println(start + " " + end);
}
public static void main(String args[])
{
int arr[] = { 1 , 2 , 3 , 4 , 5 };
int n = arr.length;
int k = 3 ;
minRange(arr, n, k);
}
}
|
Python 3
def minRange(arr, n, k):
l = 0
r = n
for i in range (n):
s = []
for j in range (i, n) :
s.append(arr[j])
if ( len (s) = = k):
if ((j - i) < (r - l)) :
r = j
l = i
break
if (j = = n):
break
if (l = = 0 and r = = n):
print ( "Invalid k" )
else :
print (l, r)
if __name__ = = "__main__" :
arr = [ 1 , 2 , 3 , 4 , 5 ]
n = len (arr)
k = 3
minRange(arr, n, k)
|
C#
using System;
using System.Collections.Generic;
public class GFG
{
public static void minRange( int [] arr, int n, int k)
{
int l = 0, r = n;
for ( int i = 0; i < n; i++)
{
ISet< int > s = new HashSet< int >();
int j;
for (j = i; j < n; j++)
{
s.Add(arr[j]);
if (s.Count == k)
{
if ((j - i) < (r - l))
{
r = j;
l = i;
}
break ;
}
}
if (j == n)
{
break ;
}
}
if (l == 0 && r == n)
{
Console.WriteLine( "Invalid k" );
}
else
{
Console.WriteLine(l + " " + r);
}
}
public static void Main( string [] args)
{
int [] arr = new int [] {1, 2, 3, 4, 5};
int n = arr.Length;
int k = 3;
minRange(arr, n, k);
}
}
|
Javascript
<script>
function minRange(arr,n,k)
{
let l = 0, r = n;
for (let i = 0; i < n; i++)
{
let s = new Set();
let j;
for (j = i; j < n; j++)
{
s.add(arr[j]);
if (s.size == k)
{
if ((j - i) < (r - l))
{
r = j;
l = i;
}
break ;
}
}
if (j == n)
break ;
}
if (l == 0 && r == n)
document.write( "Invalid k" );
else
document.write(l + " " + r);
}
let arr=[1, 2, 3, 4, 5];
let n = arr.length;
let k = 3;
minRange(arr, n, k);
</script>
|
Time Complexity : O(N^2) ,where N is the number of elements in the array. Every time picking the end points of the subarray using two nested loops(one inside another) makes the time complexity O(N^2).
Space Complexity : O(N), In the worst case, we can have all ‘N’ elements in our set.
Approach 2 : (Sliding Window Approach)
Optimization is get rid of the repeated work while making all subarray, all subarray will not help to find the resultant. The approach is –
Steps :
- Initialize a map to store the frequencies of each element.
- Taking two variables as taken before : start and end of the required subarray.
- And here we are using i and j as the starting and ending index of the window respectively, initializing as i = 0 and j = 0.
- Will traverse the array while the ending pointer of our window reach the end of given array. i.e. while( j < n)
- Add the current element to the map map[ arr[j] ]++ and make j pointing to the next index
- Consider the window [ i, j-1 ] (reason for ‘j-1’ is as we incremented the value of ‘j’ just after insertion in last step) check whether its size is equal to k
- If window size is lesser than k then continue
- But if window size == k, then check its length whether it is the resultant subarray or not.
- After that we need to move our window, but in order to move our window, we have to check the starting element of our current window (i.e. i-th). If the i-th element is having a frequency of 1 then erase it from the map and else decrease its frequency by 1. And increase the i-value. Make i to point to the next element.
( For understanding the reason of erase and decreasing frequency, take an example : 4 2 2 3 4 4 3 and k = 3 when we are dealing with the window 2 2 3 4 then ‘i’ would have pointed to the start of window (first 2) and ‘j’ would have pointed to the last of window (at 4). Now while moving forward (by one position), if the window totally erase 2 from the map, (and make window 2 3 4 4) then map would contain the information that 2 is not in the map but it is wrong so we will decrease the count of 2. Similarly, in case of having frequency == 1, and about to leave the window, the map should not contain the frequency of the element which not there in the window. )
Implementation:
C++
#include <bits/stdc++.h>
using namespace std;
void minRange( int arr[], int n, int k)
{
int start = 0, end = n;
unordered_map< int , int > map;
int i = 0, j = 0;
while (j < n) {
map[arr[j]]++;
j++;
if (map.size() < k)
continue ;
while (map.size() == k) {
int windowLen = (j - 1) - i + 1;
int subArrayLen = end - start + 1;
if (subArrayLen > windowLen) {
start = i;
end = j - 1;
}
if (map[arr[i]] == 1)
map.erase(arr[i]);
else
map[arr[i]]--;
i++;
}
}
if (start == 0 && end == n)
cout << "Invalid k" << endl;
else
cout << start << " " << end << endl;
}
int main()
{
int arr[] = { 1, 1, 2, 2, 3, 3, 4, 5 };
int n = sizeof (arr) / sizeof (arr[0]);
int k = 3;
minRange(arr, n, k);
return 0;
}
|
Java
import java.util.*;
class GFG {
static void minRange( int arr[], int n, int k)
{
int start = 0 , end = n;
HashMap<Integer, Integer> map = new HashMap<>();
int i = 0 , j = 0 ;
while (j < n) {
map.put(arr[j], map.getOrDefault(arr[j], 0 ) + 1 );
j++;
if (map.size() < k)
continue ;
while (map.size() == k)
{
int windowLen = (j - 1 ) - i + 1 ;
int subArrayLen = end - start + 1 ;
if (windowLen < subArrayLen) {
start = i;
end = j - 1 ;
}
if (map.get(arr[i]) == 1 )
map.remove(arr[i]);
else
map.put(arr[i], map.get(arr[i]) - 1 );
i++;
}
}
if (start == 0 && end == n)
System.out.println( "Invalid k" );
else
System.out.println(start + " " + end);
}
public static void main(String[] args)
{
int arr[] = { 1 , 1 , 2 , 2 , 3 , 3 , 4 , 5 };
int n = arr.length;
int k = 3 ;
minRange(arr, n, k);
}
}
|
Python3
from collections import defaultdict
def minRange(arr, n, k):
l, r = 0 , n
i = 0
j = - 1
hm = defaultdict( lambda : 0 )
while i < n:
while j < n:
j + = 1
if len (hm) < k and j < n:
hm[arr[j]] + = 1
if len (hm) = = k and ((r - l) > = (j - i)):
l, r = i, j
break
if len (hm) < k:
break
while len (hm) = = k:
if hm[arr[i]] = = 1 :
del (hm[arr[i]])
else :
hm[arr[i]] - = 1
i + = 1
if len (hm) = = k and (r - l) > = (j - i):
l, r = i, j
if hm[arr[i]] = = 1 :
del (hm[arr[i]])
else :
hm[arr[i]] - = 1
i + = 1
if l = = 0 and r = = n:
print ( "Invalid k" )
else :
print (l, r)
if __name__ = = "__main__" :
arr = [ 1 , 1 , 2 , 2 , 3 , 3 , 4 , 5 ]
n = len (arr)
k = 3
minRange(arr, n, k)
|
C#
using System;
using System.Collections.Generic;
class GFG{
static void minRange( int []arr,
int n, int k)
{
int l = 0, r = n;
int j = -1;
Dictionary< int ,
int > hm = new Dictionary< int ,
int >();
for ( int i = 0; i < n; i++)
{
while (j < n)
{
j++;
if (j < n && hm.Count < k)
if (hm.ContainsKey(arr[j]))
hm[arr[j]] = hm[arr[j]] + 1;
else
hm.Add(arr[j], 1);
if (hm.Count == k &&
((r - l) >= (j - i)))
{
l = i;
r = j;
break ;
}
}
if (hm.Count < k)
break ;
while (hm.Count == k)
{
if (hm.ContainsKey(arr[i]) &&
hm[arr[i]] == 1)
hm.Remove(arr[i]);
else
{
if (hm.ContainsKey(arr[i]))
hm[arr[i]] = hm[arr[i]] - 1;
}
i++;
if (hm.Count == k &&
(r - l) >= (j - i))
{
l = i;
r = j;
}
}
if (hm.ContainsKey(arr[i]) &&
hm[arr[i]] == 1)
hm.Remove(arr[i]);
else
if (hm.ContainsKey(arr[i]))
hm[arr[i]] = hm[arr[i]] - 1;
}
if (l == 0 && r == n)
Console.WriteLine( "Invalid k" );
else
Console.WriteLine(l + " " + r);
}
public static void Main(String[] args)
{
int []arr = {1, 1, 2, 2,
3, 3, 4, 5};
int n = arr.Length;
int k = 3;
minRange(arr, n, k);
}
}
|
Javascript
<script>
function minRange(arr,n,k)
{
let l = 0, r = n;
let j = -1;
let hm = new Map();
for (let i = 0; i < n; i++)
{
while (j < n)
{
j++;
if (j < n && hm.size < k)
{
if (hm.has(arr[j]))
hm.set(arr[j],
hm.get(arr[j]) + 1);
else
hm.set(arr[j],1);
}
if (hm.size == k &&
((r - l) >= (j - i)))
{
l = i;
r = j;
break ;
}
}
if (hm.size < k)
break ;
while (hm.size == k)
{
if (hm.has(arr[i]) && hm.get(arr[i]) == 1)
hm. delete (arr[i]);
else
if (hm.has(arr[i]))
hm.set(arr[i],hm.get(arr[i]) - 1);
i++;
if (hm.size == k &&
(r - l) >= (j - i))
{
l = i;
r = j;
}
}
if (hm.has(arr[i]) && hm.get(arr[i]) == 1)
hm. delete (arr[i]);
else
if (hm.has(arr[i]))
hm.set(arr[i],hm.get(arr[i]) - 1);
}
if (l == 0 && r == n)
document.write( "Invalid k" );
else
document.write(l + " " + r);
}
let arr=[1, 1, 2, 2, 3, 3, 4, 5];
let n = arr.length;
let k = 3;
minRange(arr, n, k);
</script>
|
Time Complexity : O(N) ,where N is the number of elements in the array. In the worst case, each element will be added once and removed once from the map.
Space Complexity : O(K), In the worst case, we can have only ‘K’ elements in our map.
Please Login to comment...