Kth Smallest in Subarray Queries

Last Updated : 14 Sep, 2026

Given an integer array arr[] and a 2D array queries[][] of size q, where each query is represented as [l, r, k], find the kth smallest element in the subarray nums[l...r] (using 1-based indexing).

Return an array containing the answer for each query in the same order.

Examples:  

Input: arr[] = [4, 1, 2, 2, 3], queries[][] = [[1, 5, 2], [3, 5, 3]]
Output: [2, 3]
Explanation: For the 1st query 2nd smallest in [1, 5] is 2. For the 2nd query 3rd smallest in [3, 5] is 3.

Input: arr[] = [1, 2, 3, 4, 5], queries[][] = [[2, 5, 1]]
Output: [2]
Explanation: The 1st smallest in [2, 5] is 2.

Try It Yourself
redirect icon

[Naive Approach] Brute Force Approach

The simplest way to find the kth smallest element is to consider only the elements present in the queried subarray. For each query, copy the elements from l to r into a temporary array and sort it in non-decreasing order. The kth element of this sorted array is the answer.

Note: We can also use max heap of size k instead of sorting the entire subarray. For every element in the range:

  • Insert it if the heap contains fewer than k elements.
  • Otherwise, if the current element is smaller than the heap's maximum, remove the maximum and insert the current element.
  • After processing the range, the top of the max-heap is the kth smallest element.

But, this is almost same in time and space complexity to the sorting approach and hence, will give time limit exceeded for larger inputs similar to sorting approach. Therefore, we use a Persistent Segment Tree for the efficient approach.

  • For each query [l, r, k], create a temporary array.
  • Copy all elements from arr[l] to arr[r] into it.
  • Sort the temporary array in non-decreasing order.
  • The element at index k - 1 is the kth smallest element.
  • Add this element to the result array.
  • Repeat for all queries.
C++
#include <bits/stdc++.h>
using namespace std;

vector<int> findQuery(vector<int> &arr, vector<vector<int>> &queries)
{
    vector<int> result;

    // Process each query independently.
    for (auto &q : queries)
    {
        int l = q[0];
        int r = q[1];
        int k = q[2];

        // Stores the elements of the current subarray.
        vector<int> temp;

        // Copy elements from the given range.
        for (int i = l - 1; i < r; i++)
        {
            temp.push_back(arr[i]);
        }

        // Sort the subarray in non-decreasing order.
        sort(temp.begin(), temp.end());

        // The kth smallest element is at index k - 1.
        result.push_back(temp[k - 1]);
    }

    return result;
}

int main()
{
    vector<int> arr = {4, 1, 2, 2, 3};
    vector<vector<int>> queries = {{1, 5, 2}, {3, 5, 3}};

    vector<int> result = findQuery(arr, queries);

    for (int x : result)
    {
        cout << x << " ";
    }

    cout << endl;

    return 0;
}
Java
import java.util.*;

class GFG {
    static ArrayList<Integer> findQuery(int[] arr, int[][] queries)
    {
        ArrayList<Integer> result = new ArrayList<>();

        // Process each query independently.
        for (int[] q : queries) {
            int l = q[0];
            int r = q[1];
            int k = q[2];

            // Stores the elements of the current subarray.
            ArrayList<Integer> temp = new ArrayList<>();

            // Copy the elements from the given range.
            for (int i = l - 1; i < r; i++) {
                temp.add(arr[i]);
            }

            // Sort the subarray in non-decreasing order.
            Collections.sort(temp);

            // The kth smallest element is at index k - 1.
            result.add(temp.get(k - 1));
        }

        return result;
    }

    public static void main(String[] args)
    {
        int[] arr = { 4, 1, 2, 2, 3 };
        int[][] queries = { { 1, 5, 2 }, { 3, 5, 3 } };

        ArrayList<Integer> result = findQuery(arr, queries);

        for (int x : result) {
            System.out.print(x + " ");
        }

        System.out.println();
    }
}
Python
def findQuery(arr, queries):
    result = []

    # Process each query independently.
    for q in queries:
        l = q[0]
        r = q[1]
        k = q[2]

        # Stores the elements of the current subarray.
        temp = []

        # Copy the elements from the given range.
        for i in range(l - 1, r):
            temp.append(arr[i])

        # Sort the subarray in non-decreasing order.
        temp.sort()

        # The kth smallest element is at index k - 1.
        result.append(temp[k - 1])

    return result


# Driver Code
if __name__ == "__main__":
    arr = [4, 1, 2, 2, 3]
    queries = [[1, 5, 2], [3, 5, 3]]

    result = findQuery(arr, queries)

    for x in result:
        print(x, end=" ")

print()
C#
using System;
using System.Collections.Generic;

class GFG {
    static List<int> findQuery(int[] arr, int[][] queries)
    {
        List<int> result = new List<int>();

        // Process each query independently.
        foreach(int[] q in queries)
        {
            int l = q[0];
            int r = q[1];
            int k = q[2];

            // Stores the elements of the current subarray.
            List<int> temp = new List<int>();

            // Copy the elements from the given range.
            for (int i = l - 1; i < r; i++) {
                temp.Add(arr[i]);
            }

            // Sort the subarray in non-decreasing order.
            temp.Sort();

            // The kth smallest element is at index k - 1.
            result.Add(temp[k - 1]);
        }

        return result;
    }

    public static void Main()
    {
        int[] arr = { 4, 1, 2, 2, 3 };
        int[][] queries = { new int[] { 1, 5, 2 },
                            new int[] { 3, 5, 3 } };

        List<int> result = findQuery(arr, queries);

        foreach(int x in result) { Console.Write(x + " "); }

        Console.WriteLine();
    }
}
JavaScript
function findQuery(arr, queries)
{
    let result = [];

    // Process each query independently.
    for (let q of queries) {
        let l = q[0];
        let r = q[1];
        let k = q[2];

        // Stores the elements of the current subarray.
        let temp = [];

        // Copy the elements from the given range.
        for (let i = l - 1; i < r; i++) {
            temp.push(arr[i]);
        }

        // Sort the subarray in non-decreasing order.
        temp.sort((a, b) => a - b);

        // The kth smallest element is at index k - 1.
        result.push(temp[k - 1]);
    }

    return result;
}

// Driver Code
let arr = [ 4, 1, 2, 2, 3 ];
let queries = [ [ 1, 5, 2 ], [ 3, 5, 3 ] ];

let result = findQuery(arr, queries);
let ans = "";
for (let x of result) {
    ans += x + " ";
}

console.log(ans.trim());

Output
2 3 

Time Complexity: O(q * S log S) where q is the size of queries and S = (r − l + 1) be the size of the queried subarray.
Auxiliary Space: O(q * S)

[Expected Approach] Using Segment Tree

The idea is to use a segment tree to quickly find where the kth smallest element lies. Instead of sorting every subarray, we keep track of how many elements fall in different value ranges.

For each query, we compare the information of the array up to r with the information up to l - 1, so that we get only the elements from arr[l...r]. Now, we check how many of these elements are in the smaller half of the values.

If there are at least k, the answer must be there, so we move to the left half; otherwise, we move to the right half and reduce k. We continue this until only one value is left, which is the kth smallest element.

  • Coordinate Compression: Copy all array elements into values, sort them, and remove duplicates. This maps every distinct value to a position from 0 to m - 1.
  • Initialize Persistent Tree: Create root[i] for every prefix of the array, where root[i] represents the frequency of values in the first i elements. Node 0 is the empty/null node.
  • Build Versions: For every arr[i], find its compressed position and create a new tree version from root[i]. Only the nodes along the path to that value are copied and updated; all other nodes are shared.
  • Process Each Query: For query [l, r, k], use root[r] and root[l - 1]. Their frequency difference represents only the elements present in arr[l...r].
  • Find kth Smallest: Count the elements in the left half using the difference between the two roots. If this count is at least k, move to the left half; otherwise, move to the right half and update k = k - count_left.
  • Return Answer: Continue until reaching a leaf. Its compressed position represents the kth smallest element. Use values[pos] to convert it back to the original value and store it in the result.
C++
#include <bits/stdc++.h>
using namespace std;

// Represents a node of the persistent segment tree.
struct Node
{
    int count;
    int left, right;
};

vector<Node> tree;
vector<int> roots;
int nodes_cnt;

// Inserts a value into the new version of the segment tree.
int insert(int prev_root, int low, int high, int pos)
{
    // Create a new node by copying the previous node.
    int cur = nodes_cnt++;
    tree[cur] = tree[prev_root];

    // Increase the frequency of the current value.
    tree[cur].count++;

    // If we reach the leaf, return the new node.
    if (low == high)
        return cur;

    int mid = low + (high - low) / 2;

    // Insert into the left or right half.
    if (pos <= mid)
    {
        tree[cur].left = insert(tree[prev_root].left, low, mid, pos);
    }
    else
    {
        tree[cur].right = insert(tree[prev_root].right, mid + 1, high, pos);
    }

    return cur;
}

// Finds the kth smallest value using two persistent versions.
int query(int root_l, int root_r, int low, int high, int k)
{
    // When we reach a leaf, its position is the answer.
    if (low == high)
        return low;

    int mid = low + (high - low) / 2;

    // Count elements in the left half of arr[l...r].
    int count_left = tree[tree[root_r].left].count - tree[tree[root_l].left].count;

    // If the left half contains at least k elements,
    // the answer lies in the left half.
    if (count_left >= k)
    {
        return query(tree[root_l].left, tree[root_r].left, low, mid, k);
    }

    // Otherwise, the answer lies in the right half.
    return query(tree[root_l].right, tree[root_r].right, mid + 1, high, k - count_left);
}

vector<int> findQuery(vector<int> &arr, vector<vector<int>> &queries)
{
    int n = arr.size();

    // Coordinate compression of the array values.
    vector<int> values = arr;

    // Store all distinct values in sorted order.
    sort(values.begin(), values.end());
    values.erase(unique(values.begin(), values.end()), values.end());

    int m = values.size();

    // Each insertion creates at most O(log m) new nodes.
    int max_nodes = n * 20 + 5;

    tree.assign(max_nodes, {0, 0, 0});

    // root[i] represents the first i elements of the array.
    roots.assign(n + 1, 0);

    // Node 0 is used as the null/empty node.
    nodes_cnt = 1;

    // Build persistent segment tree versions.
    for (int i = 0; i < n; i++)
    {
        // Find the compressed position of arr[i].
        int pos = lower_bound(values.begin(), values.end(), arr[i]) - values.begin();

        // Create the next version from the previous version.
        roots[i + 1] = insert(roots[i], 0, m - 1, pos);
    }

    vector<int> result;
    result.reserve(queries.size());

    // Process every query independently.
    for (auto &q : queries)
    {
        int l = q[0];
        int r = q[1];
        int k = q[2];

        // Find the compressed position of the kth smallest value.
        int pos = query(roots[l - 1], roots[r], 0, m - 1, k);

        // Convert the compressed position back to the original value.
        result.push_back(values[pos]);
    }

    return result;
}

int main()
{
    vector<int> arr = {4, 1, 2, 2, 3};
    vector<vector<int>> queries = {{1, 5, 2}, {3, 5, 3}};

    vector<int> result = findQuery(arr, queries);
    for (int x : result)
    {
        cout << x << " ";
    }

    cout << endl;

    return 0;
}
Java
import java.util.*;

class GFG {

    // Represents a node of the persistent segment tree.
    static class Node {
        int count;
        int left, right;

        Node(int count, int left, int right)
        {
            this.count = count;
            this.left = left;
            this.right = right;
        }
    }

    static Node[] tree;
    static int[] roots;
    static int nodes_cnt;

    // Inserts a value into the new version of the segment
    // tree.
    static int insert(int prev_root, int low, int high,
                      int pos)
    {
        // Create a new node by copying the previous node.
        int cur = nodes_cnt++;

        tree[cur] = new Node(tree[prev_root].count,
                             tree[prev_root].left,
                             tree[prev_root].right);

        // Increase the frequency of the current value.
        tree[cur].count++;

        // If we reach the leaf, return the new node.
        if (low == high)
            return cur;

        int mid = low + (high - low) / 2;

        // Insert into the left or right half.
        if (pos <= mid) {
            tree[cur].left = insert(tree[prev_root].left,
                                    low, mid, pos);
        }
        else {
            tree[cur].right = insert(tree[prev_root].right,
                                     mid + 1, high, pos);
        }

        return cur;
    }

    // Finds the kth smallest value using two persistent
    // versions.
    static int query(int root_l, int root_r, int low,
                     int high, int k)
    {
        // When we reach a leaf, its position is the answer.
        if (low == high)
            return low;

        int mid = low + (high - low) / 2;

        // Count elements in the left half of arr[l...r].
        int count_left = tree[tree[root_r].left].count
                         - tree[tree[root_l].left].count;

        // If the left half contains at least k elements,
        // the answer lies in the left half.
        if (count_left >= k) {
            return query(tree[root_l].left,
                         tree[root_r].left, low, mid, k);
        }

        // Otherwise, the answer lies in the right half.
        return query(tree[root_l].right, tree[root_r].right,
                     mid + 1, high, k - count_left);
    }

    static ArrayList<Integer> findQuery(int[] arr, int[][] queries)
    {
        int n = arr.length;

        // Coordinate compression of the array values.
        int[] values = arr.clone();

        // Store all distinct values in sorted order.
        Arrays.sort(values);

        int uniqueCount = 0;

        for (int x : values) {
            if (uniqueCount == 0
                || values[uniqueCount - 1] != x) {
                values[uniqueCount++] = x;
            }
        }

        values = Arrays.copyOf(values, uniqueCount);

        int m = values.length;

        // Each insertion creates at most O(log m) new
        // nodes.
        int max_nodes = n * 20 + 5;

        tree = new Node[max_nodes];

        // Node 0 is used as the null/empty node.
        tree[0] = new Node(0, 0, 0);

        // root[i] represents the first i elements of the
        // array.
        roots = new int[n + 1];

        nodes_cnt = 1;

        // Build persistent segment tree versions.
        for (int i = 0; i < n; i++) {

            // Find the compressed position of arr[i].
            int pos = Arrays.binarySearch(values, arr[i]);

            // Create the next version from the previous
            // version.
            roots[i + 1] = insert(roots[i], 0, m - 1, pos);
        }

        ArrayList<Integer> result = new ArrayList<>();

        // Process every query independently.
        for (int[] q : queries) {
            int l = q[0];
            int r = q[1];
            int k = q[2];

            // Find the compressed position of the kth
            // smallest value.
            int pos = query(roots[l - 1], roots[r], 0,
                            m - 1, k);

            // Convert the compressed position back to the
            // original value.
            result.add(values[pos]);
        }

        return result;
    }

    public static void main(String[] args)
    {
        int[] arr = { 4, 1, 2, 2, 3 };
        int[][] queries = { { 1, 5, 2 }, { 3, 5, 3 } };

        ArrayList<Integer> result = findQuery(arr, queries);

        for (int x : result) {
            System.out.print(x + " ");
        }

        System.out.println();
    }
}
Python
# Represents a node of the persistent segment tree.
class Node:
    def __init__(self, count=0, left=0, right=0):
        self.count = count
        self.left = left
        self.right = right


tree = []
roots = []
nodes_cnt = 0


# Inserts a value into the new version of the segment tree.
def insert(prev_root, low, high, pos):
    global nodes_cnt

    # Create a new node by copying the previous node.
    cur = nodes_cnt
    nodes_cnt += 1

    tree[cur] = Node(
        tree[prev_root].count,
        tree[prev_root].left,
        tree[prev_root].right
    )

    # Increase the frequency of the current value.
    tree[cur].count += 1

    # If we reach the leaf, return the new node.
    if low == high:
        return cur

    mid = low + (high - low) // 2

    # Insert into the left or right half.
    if pos <= mid:
        tree[cur].left = insert(
            tree[prev_root].left,
            low, mid, pos
        )
    else:
        tree[cur].right = insert(
            tree[prev_root].right,
            mid + 1, high, pos
        )

    return cur


# Finds the kth smallest value using two persistent versions.
def query(root_l, root_r, low, high, k):

    # When we reach a leaf, its position is the answer.
    if low == high:
        return low

    mid = low + (high - low) // 2

    # Count elements in the left half of arr[l...r].
    count_left = (
        tree[tree[root_r].left].count
        - tree[tree[root_l].left].count
    )

    # If the left half contains at least k elements,
    # the answer lies in the left half.
    if count_left >= k:
        return query(
            tree[root_l].left,
            tree[root_r].left,
            low, mid, k
        )

    # Otherwise, the answer lies in the right half.
    return query(
        tree[root_l].right,
        tree[root_r].right,
        mid + 1, high,
        k - count_left
    )


def findQuery(arr, queries):
    global tree, roots, nodes_cnt

    n = len(arr)

    # Coordinate compression of the array values.
    values = arr[:]

    # Store all distinct values in sorted order.
    values = sorted(set(values))

    m = len(values)

    # Each insertion creates at most O(log m) new nodes.
    max_nodes = n * 20 + 5

    tree = [Node() for _ in range(max_nodes)]

    # root[i] represents the first i elements of the array.
    roots = [0] * (n + 1)

    # Node 0 is used as the null/empty node.
    nodes_cnt = 1

    # Build persistent segment tree versions.
    for i in range(n):

        # Find the compressed position of arr[i].
        pos = __import__('bisect').bisect_left(values, arr[i])

        # Create the next version from the previous version.
        roots[i + 1] = insert(
            roots[i],
            0, m - 1,
            pos
        )

    result = []

    # Process every query independently.
    for q in queries:
        l = q[0]
        r = q[1]
        k = q[2]

        # Find the compressed position of the kth smallest value.
        pos = query(
            roots[l - 1],
            roots[r],
            0, m - 1,
            k
        )

        # Convert the compressed position back to the original value.
        result.append(values[pos])

    return result


# Driver Code
if __name__ == "__main__":
    arr = [4, 1, 2, 2, 3]

    queries = [
        [1, 5, 2],
        [3, 5, 3]
    ]

    result = findQuery(arr, queries)

    for x in result:
        print(x, end=" ")

    print()
C#
using System;
using System.Collections.Generic;

// Represents a node of the persistent segment tree.
class Node {
    public int count;
    public int left, right;

    public Node(int count = 0, int left = 0, int right = 0)
    {
        this.count = count;
        this.left = left;
        this.right = right;
    }
}

class GFG {
    static Node[] tree;
    static int[] roots;
    static int nodes_cnt;

    // Inserts a value into the new version of the segment
    // tree.
    static int insert(int prev_root, int low, int high,
                      int pos)
    {
        // Create a new node by copying the previous node.
        int cur = nodes_cnt++;

        tree[cur] = new Node(tree[prev_root].count,
                             tree[prev_root].left,
                             tree[prev_root].right);

        // Increase the frequency of the current value.
        tree[cur].count++;

        // If we reach the leaf, return the new node.
        if (low == high)
            return cur;

        int mid = low + (high - low) / 2;

        // Insert into the left or right half.
        if (pos <= mid) {
            tree[cur].left = insert(tree[prev_root].left,
                                    low, mid, pos);
        }
        else {
            tree[cur].right = insert(tree[prev_root].right,
                                     mid + 1, high, pos);
        }

        return cur;
    }

    // Finds the kth smallest value using two persistent
    // versions.
    static int query(int root_l, int root_r, int low,
                     int high, int k)
    {
        // When we reach a leaf, its position is the answer.
        if (low == high)
            return low;

        int mid = low + (high - low) / 2;

        // Count elements in the left half of arr[l...r].
        int count_left = tree[tree[root_r].left].count
                         - tree[tree[root_l].left].count;

        // If the left half contains at least k elements,
        // the answer lies in the left half.
        if (count_left >= k) {
            return query(tree[root_l].left,
                         tree[root_r].left, low, mid, k);
        }

        // Otherwise, the answer lies in the right half.
        return query(tree[root_l].right, tree[root_r].right,
                     mid + 1, high, k - count_left);
    }

    static List<int> findQuery(int[] arr, int[][] queries)
    {
        int n = arr.Length;

        // Coordinate compression of the array values.
        int[] values = (int[])arr.Clone();

        // Store all distinct values in sorted order.
        Array.Sort(values);

        List<int> distinct = new List<int>();

        foreach(int x in values)
        {
            if (distinct.Count == 0
                || distinct[distinct.Count - 1] != x) {
                distinct.Add(x);
            }
        }

        values = distinct.ToArray();

        int m = values.Length;

        // Each insertion creates at most O(log m) new
        // nodes.
        int max_nodes = n * 20 + 5;

        tree = new Node[max_nodes];

        // Node 0 is used as the null/empty node.
        tree[0] = new Node();

        // root[i] represents the first i elements of the
        // array.
        roots = new int[n + 1];

        nodes_cnt = 1;

        // Build persistent segment tree versions.
        for (int i = 0; i < n; i++) {
            // Find the compressed position of arr[i].
            int pos = Array.BinarySearch(values, arr[i]);

            // Create the next version from the previous
            // version.
            roots[i + 1] = insert(roots[i], 0, m - 1, pos);
        }

        List<int> result = new List<int>();

        // Process every query independently.
        foreach(int[] q in queries)
        {
            int l = q[0];
            int r = q[1];
            int k = q[2];

            // Find the compressed position of the kth
            // smallest value.
            int pos = query(roots[l - 1], roots[r], 0,
                            m - 1, k);

            // Convert the compressed position back to the
            // original value.
            result.Add(values[pos]);
        }

        return result;
    }

    public static void Main()
    {
        int[] arr = { 4, 1, 2, 2, 3 };
        int[][] queries = { new int[] { 1, 5, 2 },
                            new int[] { 3, 5, 3 } };

        List<int> result = findQuery(arr, queries);

        foreach(int x in result) { Console.Write(x + " "); }

        Console.WriteLine();
    }
}
JavaScript
// Represents a node of the persistent segment tree.
class Node {
    constructor(count = 0, left = 0, right = 0)
    {
        this.count = count;
        this.left = left;
        this.right = right;
    }
}

let tree = [];
let roots = [];
let nodes_cnt = 0;

// Inserts a value into the new version of the segment tree.
function insert(prev_root, low, high, pos)
{
    // Create a new node by copying the previous node.
    let cur = nodes_cnt++;

    tree[cur] = new Node(tree[prev_root].count,
                         tree[prev_root].left,
                         tree[prev_root].right);

    // Increase the frequency of the current value.
    tree[cur].count++;

    // If we reach the leaf, return the new node.
    if (low === high)
        return cur;

    let mid = low + Math.floor((high - low) / 2);

    // Insert into the left or right half.
    if (pos <= mid) {
        tree[cur].left
            = insert(tree[prev_root].left, low, mid, pos);
    }
    else {
        tree[cur].right = insert(tree[prev_root].right,
                                 mid + 1, high, pos);
    }

    return cur;
}

// Finds the kth smallest value using two persistent
// versions.
function query(root_l, root_r, low, high, k)
{
    // When we reach a leaf, its position is the answer.
    if (low === high)
        return low;

    let mid = low + Math.floor((high - low) / 2);

    // Count elements in the left half of arr[l...r].
    let count_left = tree[tree[root_r].left].count
                     - tree[tree[root_l].left].count;

    // If the left half contains at least k elements,
    // the answer lies in the left half.
    if (count_left >= k) {
        return query(tree[root_l].left, tree[root_r].left,
                     low, mid, k);
    }

    // Otherwise, the answer lies in the right half.
    return query(tree[root_l].right, tree[root_r].right,
                 mid + 1, high, k - count_left);
}

function findQuery(arr, queries)
{
    let n = arr.length;

    // Coordinate compression of the array values.
    let values = [...arr ];

    // Store all distinct values in sorted order.
    values.sort((a, b) => a - b);

    values = [...new Set(values) ];

    let m = values.length;

    // Each insertion creates at most O(log m) new nodes.
    let max_nodes = n * 20 + 5;

    tree = Array.from({length : max_nodes},
                      () => new Node());

    // root[i] represents the first i elements of the array.
    roots = new Array(n + 1).fill(0);

    // Node 0 is used as the null/empty node.
    nodes_cnt = 1;

    // Build persistent segment tree versions.
    for (let i = 0; i < n; i++) {

        // Find the compressed position of arr[i].
        let pos = lowerBound(values, arr[i]);

        // Create the next version from the previous
        // version.
        roots[i + 1] = insert(roots[i], 0, m - 1, pos);
    }

    let result = [];

    // Process every query independently.
    for (let q of queries) {
        let l = q[0];
        let r = q[1];
        let k = q[2];

        // Find the compressed position of the kth smallest
        // value.
        let pos
            = query(roots[l - 1], roots[r], 0, m - 1, k);

        // Convert the compressed position back to the
        // original value.
        result.push(values[pos]);
    }

    return result;
}

// Finds the first position whose value is >= target.
function lowerBound(arr, target)
{
    let low = 0;
    let high = arr.length;

    while (low < high) {
        let mid = low + Math.floor((high - low) / 2);

        if (arr[mid] < target)
            low = mid + 1;
        else
            high = mid;
    }

    return low;
}

// Driver Code
let arr = [ 4, 1, 2, 2, 3 ];
let queries = [ [ 1, 5, 2 ], [ 3, 5, 3 ] ];

let result = findQuery(arr, queries);
let ans = "";
for (let x of result) {
    ans += x + " ";
}

console.log(ans.trim());

Output
2 3 

Time Complexity: O((n + q) * log n)
Auxiliary Space: O(n * log n)

Comment