Skip to content
Related Articles

Related Articles

Queries for number of distinct elements in a subarray | Set 2
  • Difficulty Level : Hard
  • Last Updated : 21 Oct, 2020

Given an array arr[] of N integers and Q queries. Each query can be represented by two integers L and R. The task is to find the count of distinct integers in the subarray arr[L] to arr[R].
Examples:

Input: arr[] = {1, 1, 3, 3, 5, 5, 7, 7, 9, 9 }, L = 0, R = 4 
Output: 3
Input: arr[] = { 1, 1, 2, 1, 3 }, L = 1, R = 3 
Output :

Naive approach: In this approach, we will traverse through the range l, r, and use a set to find all the distinct elements in the range and print them. 
Time Complexity: O(q*n)
Efficient approach: Idea is to form a segment tree in which the nodes will store all the distinct elements in the range. For this purpose, we can use a self-balancing BST or “set” data structure in C++. 
Each query will return the size of the set.
Below is the implementation of the above approach: 
 

C++




// C++ implementation of above approach
#include <bits/stdc++.h>
using namespace std;
 
// Each segment of the segment tree would be a set
// to maintain distinct elements
set<int>* segment;
 
// Build the segment tree
// i denotes current node, s denotes start and
// e denotes the end of range for current node
void build(int i, int s, int e, int arr[])
{
 
    // If start is equal to end then
    // insert the array element
    if (s == e) {
        segment[i].insert(arr[s]);
        return;
    }
 
    // Else divide the range into two halves
    // (start to mid) and (mid+1 to end)
    // first half will be the left node
    // and the second half will be the right node
    build(2 * i, s, (s + e) / 2, arr);
    build(1 + 2 * i, 1 + (s + e) / 2, e, arr);
 
    // Insert the sets of right and left
    // node of the segment tree
    segment[i].insert(segment[2 * i].begin(),
                      segment[2 * i].end());
 
    segment[i].insert(segment[2 * i + 1].begin(),
                      segment[2 * i + 1].end());
}
 
// Query in an range a to b
set<int> query(int node, int l, int r, int a, int b)
{
    set<int> left, right, result;
 
    // If the range is out of the bounds
    // of this segment
    if (b < l || a > r)
        return result;
 
    // If the range lies in this segment
    if (a <= l && r <= b)
        return segment[node];
 
    // Else query for the right and left
    // leaf node of this subtree
    // and insert them into the set
    left = query(2 * node, l, (l + r) / 2, a, b);
    result.insert(left.begin(), left.end());
 
    right = query(1 + 2 * node, 1 + (l + r) / 2, r, a, b);
    result.insert(right.begin(), right.end());
 
    // Return the result
    return result;
}
 
// Initialize the segment tree
void init(int n)
{
    // Get the height of the segment tree
    int h = (int)ceil(log2(n));
    h = (2 * (pow(2, h))) - 1;
 
    // Initialize the segment tree
    segment = new set<int>[h];
}
 
// Function to get the result for the
// subarray from arr[l] to arr[r]
void getDistinct(int l, int r, int n)
{
    // Query for the range set
    set<int> ans = query(1, 0, n - 1, l, r);
 
    cout << ans.size() << endl;
}
 
// Driver code
int main()
{
 
    int arr[] = { 1, 1, 2, 1, 3 };
    int n = sizeof(arr) / sizeof(arr[0]);
 
    init(n);
 
    // Bulid the segment tree
    build(1, 0, n - 1, arr);
 
    // Query in range 0 to 4
    getDistinct(0, 4, n);
 
    return 0;
}

Java




// Java implementation of above approach
import java.io.*;
import java.util.*;
 
class GFG
{
 
    // Each segment of the segment tree would be a set
    // to maintain distinct elements
    static HashSet<Integer>[] segment;
 
    // Build the segment tree
    // i denotes current node, s denotes start and
    // e denotes the end of range for current node
    static void build(int i, int s, int e, int[] arr)
    {
 
        // If start is equal to end then
        // insert the array element
        if (s == e)
        {
            segment[i].add(arr[s]);
            return;
        }
 
        // Else divide the range into two halves
        // (start to mid) and (mid+1 to end)
        // first half will be the left node
        // and the second half will be the right node
        build(2 * i, s, (s + e) / 2, arr);
        build(1 + 2 * i, 1 + (s + e) / 2, e, arr);
 
        // Insert the sets of right and left
        // node of the segment tree
        segment[i].addAll(segment[2 * i]);
        segment[i].addAll(segment[2 * i + 1]);
    }
 
    // Query in an range a to b
    static HashSet<Integer> query(int node, int l,
                                int r, int a, int b)
    {
        HashSet<Integer> left = new HashSet<>();
        HashSet<Integer> right = new HashSet<>();
        HashSet<Integer> result = new HashSet<>();
 
        // If the range is out of the bounds
        // of this segment
        if (b < l || a > r)
            return result;
 
        // If the range lies in this segment
        if (a <= l && r <= b)
            return segment[node];
 
        // Else query for the right and left
        // leaf node of this subtree
        // and insert them into the set
        left = query(2 * node, l, (l + r) / 2, a, b);
        result.addAll(left);
 
        right = query(1 + 2 * node, 1 + (l + r) / 2, r, a, b);
        result.addAll(right);
 
        // Return the result
        return result;
    }
 
    // Initialize the segment tree
    @SuppressWarnings("unchecked")
    static void init(int n)
    {
 
        // Get the height of the segment tree
        int h = (int) Math.ceil(Math.log(n) / Math.log(2));
        h = (int) (2 * Math.pow(2, h)) - 1;
 
        // Initialize the segment tree
        segment = new HashSet[h];
        for (int i = 0; i < h; i++)
            segment[i] = new HashSet<>();
    }
 
    // Function to get the result for the
    // subarray from arr[l] to arr[r]
    static void getDistinct(int l, int r, int n)
    {
 
        // Query for the range set
        HashSet<Integer> ans = query(1, 0, n - 1, l, r);
 
        System.out.println(ans.size());
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        int[] arr = { 1, 1, 2, 1, 3 };
        int n = arr.length;
 
        init(n);
 
        // Bulid the segment tree
        build(1, 0, n - 1, arr);
 
        // Query in range 0 to 4
        getDistinct(0, 4, n);
    }
}
 
// This code is contributed by
// sanjeev2552

Python3




# python3 implementation of above approach
from math import ceil,log,floor
 
# Each segment of the segment tree would be a set
# to maintain distinct elements
segment=[[] for i in range(1000)]
 
# Build the segment tree
# i denotes current node, s denotes start and
# e denotes the end of range for current node
def build(i, s, e, arr):
 
    # If start is equal to end then
    # append the array element
    if (s == e):
        segment[i].append(arr[s])
        return
 
    # Else divide the range into two halves
    # (start to mid) and (mid+1 to end)
    # first half will be the left node
    # and the second half will be the right node
    build(2 * i, s, (s + e) // 2, arr)
    build(1 + 2 * i, 1 + (s + e) // 2, e, arr)
 
    # Insert the sets of right and left
    # node of the segment tree
    segment[i].append(segment[2 * i])
 
    segment[i].append(segment[2 * i + 1])
 
# Query in an range a to b
def query(node, l, r, a, b):
    left, right, result=[],[],[]
 
    # If the range is out of the bounds
    # of this segment
    if (b < l or a > r):
        return result
 
    # If the range lies in this segment
    if (a <= l and r <= b):
        return segment[node]
 
    # Else query for the right and left
    # leaf node of this subtree
    # and append them into the set
    left = query(2 * node, l, (l + r) // 2, a, b)
    result.append(left)
 
    right = query(1 + 2 * node, 1 + (l + r) // 2, r, a, b)
    result.append(right)
 
    # Return the result
    return result
def answer(ans):
    d = {}
    for i in str(ans):
        if i not in ['[',',',']',' ']:
            d[i]=1
    return len(d)
 
# Initialize the segment tree
def init(n):
     
    # Get the height of the segment tree
    h = ceil(log(n, 2))
    h = (2 * (pow(2, h))) - 1
 
# Function to get the result for the
# subarray from arr[l] to arr[r]
def getDistinct(l, r, n):
     
    # Query for the range set
    ans = query(1, 0, n - 1, l, r)
 
    print(answer(str(ans)))
 
# Driver code
arr=[1, 1, 2, 1, 3]
n = len(arr)
 
init(n)
 
# Bulid the segment tree
build(1, 0, n - 1, arr)
 
# Query in range 0 to 4
getDistinct(0, 4, n)
 
# This code is contributed by mohit kumar 29

C#




// C# implementation of
// the above approach
using System;
using System.Collections;
using System.Collections.Generic;
class GFG{
  
// Each segment of the segment
// tree would be a set to maintain
// distinct elements
static HashSet<int>[] segment;
  
// Build the segment tree
// i denotes current node,
// s denotes start and
// e denotes the end of
// range for current node
static void build(int i, int s,
                  int e, int[] arr)
{
  // If start is equal to end then
  // insert the array element
  if (s == e)
  {
    segment[i].Add(arr[s]);
    return;
  }
 
  // Else divide the range
  // into two halves (start
  // to mid) and (mid+1 to end)
  // first half will be the left
  // node and the second half
  // will be the right node
  build(2 * i, s,
        (s + e) / 2, arr);
  build(1 + 2 * i,
        1 + (s + e) / 2,
        e, arr);
 
  // Insert the sets of
  // right and left node
  // of the segment tree
  foreach(int x in segment[2 * i])
  {
    segment[i].Add(x);   
  }
 
  foreach(int x in segment[2 * i + 1])
  {
    segment[i].Add(x);   
  }
}
  
// Query in an range a to b
static HashSet<int> query(int node, int l,
                          int r, int a, int b)
{
  HashSet<int> left = new HashSet<int>();
  HashSet<int> right = new HashSet<int>();
  HashSet<int> result = new HashSet<int>();
 
  // If the range is out
  // of the bounds
  // of this segment
  if (b < l || a > r)
    return result;
 
  // If the range lies
  // in this segment
  if (a <= l && r <= b)
    return segment[node];
 
  // Else query for the right and left
  // leaf node of this subtree
  // and insert them into the set
  left = query(2 * node,
               l, (l + r) / 2,
               a, b);
  foreach(int x in left)
  {
    result.Add(x);   
  }
 
  right = query(1 + 2 * node,
                1 + (l + r) / 2,
                r, a, b);
  foreach(int x in right)
  {
    result.Add(x);   
  }
 
  // Return the result
  return result;
}
 
// Initialize the segment tree
static void init(int n)
{
 
  // Get the height of the segment tree
  int h = (int) Math.Ceiling(Math.Log(n) /
                             Math.Log(2));
  h = (int) (2 * Math.Pow(2, h)) - 1;
 
  // Initialize the segment tree
  segment = new HashSet<int>[h];
   
  for (int i = 0; i < h; i++)
    segment[i] = new HashSet<int>();
}
 
// Function to get the result for the
// subarray from arr[l] to arr[r]
static void getDistinct(int l,
                        int r, int n)
{
  // Query for the range set
  HashSet<int> ans = query(1, 0,
                           n - 1,
                           l, r);
 
  Console.Write(ans.Count);
}
 
// Driver Code
public static void Main(string[] args)
{
  int[] arr = {1, 1, 2, 1, 3};
  int n = arr.Length;
  init(n);
 
  // Bulid the segment tree
  build(1, 0, n - 1, arr);
 
  // Query in range 0 to 4
  getDistinct(0, 4, n);
}
}
 
// This code is contributed by rutvik_56
Output: 
3

 

Time Complexity: O(q*n*log(n))

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.  To complete your preparation from learning a language to DS Algo and many more,  please refer Complete Interview Preparation Course.




My Personal Notes arrow_drop_up
Recommended Articles
Page :