Open In App

Query-Based Array Transformation

Last Updated : 17 Dec, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Given an array arr[] of size N, initially, all the elements of arr are 0, you have to process q queries of the form “L R X” denoting that fill the array from index l to index r with the number x, the task is to print the final array after all the operations have been performed.

Examples:

Input: N = 4, queries = [[1, 3, 2], [2, 4, 6], [2, 3, 7]]
Output: [2, 7, 7, 6]
Explanation: Starting with an array of [0, 0 0 0] Starting with an array of [0, 0 0 0] lets perform some operations:

  1. Query [1, 3, 2]:
    Add 2 to the elements, in the range from index 1 to index 3.
    The updated array becomes [2, 2, 2, 0].
  2. Query [2, 4, 6]:
    Add 6 to the elements in the range, from index 2 to index 4.
    The updated array becomes [2, 6, 6, 6].
  3. Query [2, 3, 7]:
    Adding 7 to the elements in the range from index 2 to index 3.
    The updated array becomes [2, 7, 7, 6].

Input: N = 4, queries = [[1, 4, 2], [2, 3, 7], [1, 2, 3]]
Output: [3, 3, 7, 2]
Explanation: Given an array, with four elements initially set to zero, we will perform a series of operations on it. Let’s go through each query step by step:

  1. Query [1, 4 2]:
    • We add 2 to all the elements in the range from index 1 to index 4. As a result, the updated array becomes [2, 2, 2, 2].
  2. Query [2, 3 7]:
    • Next, we add 7 to all the elements in the range from index 2 to index 3. This leads to an updated array of [2, 7, 7, 2].
  3. Query [1, 2, 3]:
    • Finally we add 3 to all the elements in the range from index 1 to index 2. This modifies the array again. Results in [3, 3, 7, 2].
      So after performing all these queries on our array of zeros and making adjustments at each step according to the given ranges and values specified within each query instruction set (from adding values like ‘two’ or ‘seven’, within specific ranges) we end up with a final updated array of [3, 3, 7, 2].

Naive Approach: The basic way to solve the problem is as follows:

The given approach uses a technique to handle a sequence of queries that involve making changes, to elements in an array. It keeps track of an array called “arr” which is initially filled with zeros and then proceeds to go through a list of queries. Each query in the format [L, R, X] guides the code to update a range [L, R], in the “arr” by assigning it the value X. The algorithm carries out these updates for each query resulting in the arr“.

Follow the steps to solve the problem:

  • Start by creating an array called “arr” with a size of N and initialize all its elements to zero.
  • Go through each query in the “queries” list.
  • For each query [L, R, X];
  • Iterate through the indices from L 1 to R 1 in the “arr” array.
  • Set each element within this range to the value of X.
  • Once all queries have been processed, return the modified “arr” array.

Illustrations:

Lets consider an array, with four elements initially set to zero we will perform a series of operations on it. Lets go through each query step by step:

N = 4, queries = [[1, 4, 2], [2, 3, 7], [1, 2, 3]]

  • Query [1, 4, 2]:
    • We add 2 to all the elements in the range from index 1 to index 4. As a result the updated array becomes [2, 2, 2, 2].
  • Query [2, 3, 7]:
    • Next we add 7 to all the elements in the range from index 2 to index 3. This leads to an updated array of [2, 7, 7, 2].
  • Query [1, 2, 3]:
    • Finally we add 3 to all the elements in the range from index 1 to index 2. This modifies the array again. Results in [3, 3, 7, 2].
  • So after performing all these queries on our array of zeros and making adjustments at each step according to the given ranges and values specified within each query instruction set (from adding values like ‘two’ or ‘seven’, within specific ranges) we end up with a final updated array of [3, 3, 7, 2].

Here is the implementation of the above approach:

C++




// C++ code for the above approach:
#include <iostream>
#include <vector>
 
using namespace std;
 
vector<int> DoQueries(int N, vector<vector<int> >& queries)
{
    vector<int> arr(N, 0);
 
    for (const vector<int>& query : queries) {
        int L = query[0];
        int R = query[1];
        int X = query[2];
 
        for (int i = L - 1; i < R; ++i) {
            arr[i] = X;
        }
    }
 
    return arr;
}
 
// Drivers code
int main()
{
    int N1 = 4;
    vector<vector<int> > queries1
        = { { 1, 4, 2 }, { 2, 3, 7 }, { 1, 2, 3 } };
    vector<int> result1 = DoQueries(N1, queries1);
 
    for (int value : result1) {
        cout << value << " ";
    }
    cout << endl;
 
    int N2 = 4;
    vector<vector<int> > queries2
        = { { 1, 3, 2 }, { 2, 4, 6 }, { 2, 3, 7 } };
    vector<int> result2 = DoQueries(N2, queries2);
 
    for (int value : result2) {
        cout << value << " ";
    }
    cout << endl;
 
    return 0;
}


Java




// Java code for the above approach
 
import java.util.*;
 
class GFG {
    public static List<Integer>
    doQueries(int N, List<List<Integer> > queries)
    {
        List<Integer> arr = new ArrayList<>(N);
 
        // Initialize the list with zeros
        for (int i = 0; i < N; i++) {
            arr.add(0);
        }
 
        for (List<Integer> query : queries) {
            int L = query.get(0);
            int R = query.get(1);
            int X = query.get(2);
 
            for (int i = L - 1; i < R; i++) {
                arr.set(i, X);
            }
        }
 
        return arr;
    }
 
    public static void main(String[] args)
    {
        int N1 = 4;
        List<List<Integer> > queries1
            = List.of(List.of(1, 4, 2), List.of(2, 3, 7),
                      List.of(1, 2, 3));
        List<Integer> result1 = doQueries(N1, queries1);
 
        for (int value : result1) {
            System.out.print(value + " ");
        }
        System.out.println();
 
        int N2 = 4;
        List<List<Integer> > queries2
            = List.of(List.of(1, 3, 2), List.of(2, 4, 6),
                      List.of(2, 3, 7));
        List<Integer> result2 = doQueries(N2, queries2);
 
        for (int value : result2) {
            System.out.print(value + " ");
        }
        System.out.println();
    }
}
 
// This code is contributed by Abhinav Mahajan
// (abhinav_m22).


Python3




def do_queries(N, queries):
    arr = [0] * # Initialize the list with zeros
 
    # Iterating over the queries
    for query in queries:
        L, R, X = query
        # Updating the array
        for i in range(L - 1, R):
            arr[i] = X
 
    return arr
 
# Driver code
if __name__ == "__main__":
    # Test case 1
    N1 = 4
    queries1 = [
        [1, 4, 2],
        [2, 3, 7],
        [1, 2, 3]
    ]
 
    # Function call
    result1 = do_queries(N1, queries1)
 
    for value in result1:
        print(value, end=" ")
    print()
 
    # Test case 2
    N2 = 4
    queries2 = [
        [1, 3, 2],
        [2, 4, 6],
        [2, 3, 7]
    ]
 
    # Function call
    result2 = do_queries(N2, queries2)
 
    for value in result2:
        print(value, end=" ")
    print()


C#




// C# code for the approach
 
using System;
using System.Collections.Generic;
 
class GFG
{
    public static List<int> DoQueries(int N, List<List<int>> queries)
    {
        List<int> arr = new List<int>(N);
 
        // Initialize the list with zeros
        for (int i = 0; i < N; i++)
            arr.Add(0);
         
        // Iterating over the queries
        foreach (List<int> query in queries)
        {
            int L = query[0];
            int R = query[1];
            int X = query[2];
             
            // Updating the array
            for (int i = L - 1; i < R; i++)
                arr[i] = X;
        }
 
        return arr;
    }
     
    // Driver code
    public static void Main(string[] args)
    {
         
        // Test case 1
        int N1 = 4;
        List<List<int>> queries1 = new List<List<int>>
        {
            new List<int> { 1, 4, 2 },
            new List<int> { 2, 3, 7 },
            new List<int> { 1, 2, 3 }
        };
         
        // Function call
        List<int> result1 = DoQueries(N1, queries1);
     
        foreach (int value in result1)
            Console.Write(value + " ");
        Console.WriteLine();
         
        // Test case 2
        int N2 = 4;
        List<List<int>> queries2 = new List<List<int>>
        {
            new List<int> { 1, 3, 2 },
            new List<int> { 2, 4, 6 },
            new List<int> { 2, 3, 7 }
        };
         
        // Function call
        List<int> result2 = DoQueries(N2, queries2);
 
        foreach (int value in result2)
            Console.Write(value + " ");
        Console.WriteLine();
    }
}
 
// by phasing17


Javascript




// Function to perform queries and return the resulting array
function doQueries(N, queries) {
    // Initialize an array of length N with all elements set to 0
    const arr = new Array(N).fill(0);
 
    // Iterate through each query
    for (const query of queries) {
        const L = query[0];
        const R = query[1];
        const X = query[2];
 
        // Update elements in the range [L-1, R-1] to the value X
        for (let i = L - 1; i < R; i++) {
            arr[i] = X;
        }
    }
 
    return arr;
}
 
// Driver code
const N1 = 4;
const queries1 = [
    [1, 4, 2],
    [2, 3, 7],
    [1, 2, 3]
];
const result1 = doQueries(N1, queries1);
 
console.log(result1.join(" "));
 
const N2 = 4;
const queries2 = [
    [1, 3, 2],
    [2, 4, 6],
    [2, 3, 7]
];
const result2 = doQueries(N2, queries2);
 
console.log(result2.join(" "));


Output

3 3 7 2 
2 7 7 6 














Time Complexity Analysis:

  • The time complexity, for initializing the “arr” array is O(N).
  • Processing each query takes O(R. L + 1) time, where R and L represent the right and left indices of the query range.
  • In the worst-case scenario, each query could potentially cover the array. Hence processing all queries would take a total time complexity of
    O(q*N) where q represents the number of queries.
  • Overall considering both initialization and processing time complexities we can simplify it to O(q*N) where N represents the size of the array and q is the number of queries.

Space Complexity Analysis:

  • The size of the “arr” array which takes up O(N) space.
  • The memory required for storing all queries in a list contributes to a space complexity of O(q) where q represents the number of queries.
  • Therefore when considering both factors we can conclude that overall space complexity is O(N + q).To summarize the code has a time complexity of O(q * N) and a space complexity of O(N + q) where N represents the array size and q represents the number of queries.

Efficient Approach: To solve the problem follow the below idea:

  • Insert numbers from 1 to N in the set which denote indices.
  • Process queries in a reverse manner and for each query find the iterators in the set for elements greater than or equal to L and elements smaller than equal to R then you update the range between the two iterators we got and then after updating we delete the values from itr1 to itr2 from set this way each index is traverse only once in all queries.

Steps to implement above Idea:

  • Fill the indices” with numbers ranging from 1 to N representing the indices that are currently available.
  • Next we’ll go through the queries in reverse order. This way we ensure that any changes made by queries are the final changes.
  • For each query retrieve and note down three things; the left bound (L), right bound (R) and value (X). These values will be crucial for updating our array. Additionally locate iterators Itr1,and Itr2 within the indices.”
  • Update the range between itr1 and itr2 in our array using X as their value. Afterward remove these processed indices (from itr1, to itr2) from our indices.”
  • Once all queries have been processed successfully our array “arr” will contain the result.

Below is the implementation in C++:

C++




// C++ code for the above approach:
#include <iostream>
#include <set>
#include <vector>
 
using namespace std;
 
vector<int> DoQueries(int N, vector<vector<int> >& queries)
{
    vector<int> arr(N, 0);
    set<int> indices;
 
    // Initialize the set with indices
    // from 1 to N
    for (int i = 1; i <= N; ++i) {
        indices.insert(i);
    }
 
    // Process queries in reverse order
    for (int q = queries.size() - 1; q >= 0; --q) {
        int L = queries[q][0];
        int R = queries[q][1];
        int X = queries[q][2];
 
        auto itr1 = indices.lower_bound(L);
        auto itr2 = indices.upper_bound(R);
 
        // Update the range between itr1 and
        // itr2 with value X
        for (auto it = itr1; it != itr2; ++it) {
            arr[*it - 1] = X;
        }
 
        // Erase the processed indices
        // from the set
        indices.erase(itr1, itr2);
    }
 
    return arr;
}
 
// Drivers code
int main()
{
    int N1 = 4;
    vector<vector<int> > queries1
        = { { 1, 4, 2 }, { 2, 3, 7 }, { 1, 2, 3 } };
    vector<int> result1 = DoQueries(N1, queries1);
 
    for (int value : result1) {
        cout << value << " ";
    }
    cout << endl;
 
    int N2 = 4;
    vector<vector<int> > queries2
        = { { 1, 3, 2 }, { 2, 4, 6 }, { 2, 3, 7 } };
    vector<int> result2 = DoQueries(N2, queries2);
 
    for (int value : result2) {
        cout << value << " ";
    }
    cout << endl;
 
    return 0;
}


Java




import java.util.*;
 
public class Queries {
    public static int[] doQueries(int N, int[][] queries) {
        int[] arr = new int[N];
        Set<Integer> indices = new HashSet<>();
 
        // Initialize the set with indices from 1 to N
        for (int i = 1; i <= N; ++i) {
            indices.add(i);
        }
 
        // Process queries in reverse order
        for (int q = queries.length - 1; q >= 0; --q) {
            int L = queries[q][0];
            int R = queries[q][1];
            int X = queries[q][2];
 
            // Update the range between L and R with value X
            for (int it = L; it <= R; ++it) {
                if (indices.contains(it)) {
                    arr[it - 1] = X;
                    indices.remove(it);
                }
            }
        }
 
        return arr;
    }
 
    public static void main(String[] args) {
        int N1 = 4;
        int[][] queries1 = { { 1, 4, 2 }, { 2, 3, 7 }, { 1, 2, 3 } };
        int[] result1 = doQueries(N1, queries1);
        System.out.println(Arrays.toString(result1));
 
        int N2 = 4;
        int[][] queries2 = { { 1, 3, 2 }, { 2, 4, 6 }, { 2, 3, 7 } };
        int[] result2 = doQueries(N2, queries2);
        System.out.println(Arrays.toString(result2));
    }
}


Python3




def do_queries(N, queries):
    arr = [0] * N
    indices = set(range(1, N + 1))
 
    # Process queries in reverse order
    for q in reversed(queries):
        L, R, X = q
 
        # Update the range between L and R with value X
        for it in range(L, R + 1):
            if it in indices:
                arr[it - 1] = X
                indices.remove(it)
 
    return arr
 
# Main function
if __name__ == "__main__":
    N1 = 4
    queries1 = [[1, 4, 2], [2, 3, 7], [1, 2, 3]]
    result1 = do_queries(N1, queries1)
    print(*result1)
 
    N2 = 4
    queries2 = [[1, 3, 2], [2, 4, 6], [2, 3, 7]]
    result2 = do_queries(N2, queries2)
    print(*result2)


C#




using System;
using System.Collections.Generic;
 
class Program
{
    // Function to process queries and update the array
    static List<int> DoQueries(int N, List<List<int>> queries)
    {
        // Create an array of size N initialized with 0s
        List<int> arr = new List<int>(new int[N]);
        // Create a sorted set to store indices
        SortedSet<int> indices = new SortedSet<int>();
 
        // Initialize the set with indices from 1 to N
        for (int i = 1; i <= N; ++i)
        {
            indices.Add(i);
        }
 
        // Process queries in reverse order
        for (int q = queries.Count - 1; q >= 0; --q)
        {
            // Extract L, R, and X values from the query
            int L = queries[q][0];
            int R = queries[q][1];
            int X = queries[q][2];
 
            // Get the subset of indices between L and R (inclusive)
            var itr1 = indices.GetViewBetween(L, R + 1).GetEnumerator();
 
            // Update array elements in the specified range with value X
            while (itr1.MoveNext())
            {
                arr[itr1.Current - 1] = X;
            }
 
            // Erase the processed indices from the set
            indices.RemoveWhere(x => x >= L && x <= R);
        }
 
        return arr;
    }
 
    static void Main(string[] args)
    {
        int N1 = 4;
        List<List<int>> queries1 = new List<List<int>>
        {
            new List<int> { 1, 4, 2 },
            new List<int> { 2, 3, 7 },
            new List<int> { 1, 2, 3 }
        };
        // Process queries for N1 and store the result
        List<int> result1 = DoQueries(N1, queries1);
 
        // Print the result of queries1
        foreach (int value in result1)
        {
            Console.Write(value + " ");
        }
        Console.WriteLine();
 
        int N2 = 4;
        List<List<int>> queries2 = new List<List<int>>
        {
            new List<int> { 1, 3, 2 },
            new List<int> { 2, 4, 6 },
            new List<int> { 2, 3, 7 }
        };
        // Process queries for N2 and store the result
        List<int> result2 = DoQueries(N2, queries2);
 
        // Print the result of queries2
        foreach (int value in result2)
        {
            Console.Write(value + " ");
        }
        Console.WriteLine();
    }
}


Javascript




function doQueries(N, queries) {
    let arr = new Array(N).fill(0);
    let indices = new Set();
 
    // Initialize the set with indices from 1 to N
    for (let i = 1; i <= N; ++i) {
        indices.add(i);
    }
 
    // Process queries in reverse order
    for (let q = queries.length - 1; q >= 0; --q) {
        let L = queries[q][0];
        let R = queries[q][1];
        let X = queries[q][2];
 
        // Update the range between L and R with value X
        for (let it = L; it <= R; ++it) {
            if (indices.has(it)) {
                arr[it - 1] = X;
                indices.delete(it);
            }
        }
    }
 
    return arr;
}
 
// Test cases
let N1 = 4;
let queries1 = [[1, 4, 2], [2, 3, 7], [1, 2, 3]];
let result1 = doQueries(N1, queries1);
console.log(result1);
 
let N2 = 4;
let queries2 = [[1, 3, 2], [2, 4, 6], [2, 3, 7]];
let result2 = doQueries(N2, queries2);
console.log(result2);


Output

3 3 7 2 
2 7 7 6 














Time Complexity: O(QlogN + Q), with Q representing the number of queries and N representing the length of the arr array.
Auxiliary Space: O(N), where N is number of length of the array.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads