2113. Elements in Array After Removing and Replacing Elements
Problem Description
You have a 0-indexed integer array nums that undergoes a cyclic transformation process starting at minute 0:
Phase 1 - Removal Phase: Every minute, the leftmost element is removed from nums until the array becomes empty. This takes n minutes (where n is the length of the original array).
Phase 2 - Restoration Phase: Every minute, one element is appended back to the end of nums in the same order they were removed, until the original array is restored. This also takes n minutes.
This entire cycle of 2n minutes repeats indefinitely.
For example, with nums = [0,1,2]:
- Minute 0:
[0,1,2](original) - Minute 1:
[1,2](removed 0) - Minute 2:
[2](removed 1) - Minute 3:
[](removed 2) - Minute 4:
[0](added back 0) - Minute 5:
[0,1](added back 1) - Minute 6:
[0,1,2](added back 2, cycle complete) - Minute 7:
[1,2](cycle repeats...)
You're given a 2D array queries where each queries[j] = [time_j, index_j] asks: "What is the value at position index_j in the array at minute time_j?"
For each query, return:
- The value
nums[index_j]ifindex_jis a valid index at minutetime_j -1ifindex_jis out of bounds at minutetime_j
The solution leverages the cyclic nature of the transformation. Since the pattern repeats every 2n minutes, we can use modulo arithmetic (t % 2n) to determine the state of the array at any given time:
- When
t < n: The array is in the removal phase withn - telements remaining, starting from indextof the original array - When
t > n: The array is in the restoration phase witht - nelements restored, which are the firstt - nelements of the original array - When
t = n: The array is empty - When
t = 0ort = 2n: The array is in its original state
Intuition
The key insight is recognizing that the array transformation follows a predictable, cyclic pattern that repeats every 2n minutes. Instead of simulating the entire process minute by minute, we can directly calculate what the array looks like at any given time.
Let's think about what happens during one complete cycle:
- For the first
nminutes (removal phase), we're removing elements from the left, which is equivalent to the array "sliding left" - the element at position 0 in the current array was originally at positiontin the original array - For the next
nminutes (restoration phase), we're building the array back up from empty, adding elements in their original order
Since the pattern repeats every 2n minutes, we can use modulo arithmetic to map any time t to its equivalent position within a single cycle: t % (2n).
Now, for any query at time t asking for index i:
During removal phase (t < n):
- The array has
n - telements remaining - These elements are the last
n - telements of the original array - The element at index
iin the current array corresponds to indexi + tin the original array - So if
i < n - t, the answer isnums[i + t]
During restoration phase (t > n):
- The array has
t - nelements (we've added backt - nelements) - These are the first
t - nelements of the original array in their original positions - So if
i < t - n, the answer isnums[i]
At the boundary (t = n):
- The array is empty, so any query returns
-1
This mathematical relationship allows us to answer each query in O(1) time without actually simulating the transformation process.
Solution Implementation
1class Solution:
2 def elementInNums(self, nums: List[int], queries: List[List[int]]) -> List[int]:
3 """
4 Find elements at specific indices after time-based transformations.
5
6 The array undergoes a cyclic pattern every 2n time units:
7 - First half (0 to n-1): Remove elements from front one by one
8 - Second half (n to 2n-1): Add elements back from front one by one
9
10 Args:
11 nums: Original array of integers
12 queries: List of [time, index] pairs to query
13
14 Returns:
15 List of elements at queried positions, -1 if index out of bounds
16 """
17 array_length = len(nums)
18 num_queries = len(queries)
19
20 # Initialize result array with -1 (default for out of bounds)
21 result = [-1] * num_queries
22
23 # Process each query
24 for query_idx, (time, target_index) in enumerate(queries):
25 # Normalize time to within one cycle (0 to 2n-1)
26 time_in_cycle = time % (2 * array_length)
27
28 # First half of cycle: array shrinking from the front
29 if time_in_cycle < array_length:
30 # Check if target index is within current array bounds
31 # Array has (n - time_in_cycle) elements starting from index time_in_cycle
32 if target_index < array_length - time_in_cycle:
33 # Map target index to original array position
34 result[query_idx] = nums[target_index + time_in_cycle]
35
36 # Second half of cycle: array growing from the front
37 elif time_in_cycle > array_length:
38 # Check if target index is within current array bounds
39 # Array has (time_in_cycle - n) elements starting from index 0
40 if target_index < time_in_cycle - array_length:
41 # Direct mapping since array starts from beginning
42 result[query_idx] = nums[target_index]
43
44 # When time_in_cycle == array_length, array is empty, result stays -1
45
46 return result
471class Solution {
2 public int[] elementInNums(int[] nums, int[][] queries) {
3 // Get the length of the original array and number of queries
4 int arrayLength = nums.length;
5 int queryCount = queries.length;
6
7 // Initialize result array to store answers for each query
8 int[] result = new int[queryCount];
9
10 // Process each query
11 for (int queryIndex = 0; queryIndex < queryCount; queryIndex++) {
12 // Default value is -1 (element doesn't exist)
13 result[queryIndex] = -1;
14
15 // Extract time and index from current query
16 int time = queries[queryIndex][0];
17 int requestedIndex = queries[queryIndex][1];
18
19 // Normalize time to be within one complete cycle (0 to 2n-1)
20 time = time % (2 * arrayLength);
21
22 // First half of cycle: array shrinks from left (elements removed from front)
23 // At time t, first t elements are removed, so array starts from index t
24 if (time < arrayLength && requestedIndex < arrayLength - time) {
25 result[queryIndex] = nums[requestedIndex + time];
26 }
27 // Second half of cycle: array grows from left (elements added back to front)
28 // At time t, (t - n) elements have been added back from the beginning
29 else if (time > arrayLength && requestedIndex < time - arrayLength) {
30 result[queryIndex] = nums[requestedIndex];
31 }
32 // When time == arrayLength, array is empty, so result remains -1
33 }
34
35 return result;
36 }
37}
381class Solution {
2public:
3 vector<int> elementInNums(vector<int>& nums, vector<vector<int>>& queries) {
4 // Get the size of the array and number of queries
5 int arraySize = nums.size();
6 int queryCount = queries.size();
7
8 // Initialize result vector with -1 (default for out-of-bounds)
9 vector<int> result(queryCount, -1);
10
11 // Process each query
12 for (int queryIndex = 0; queryIndex < queryCount; ++queryIndex) {
13 // Extract time and index from current query
14 int time = queries[queryIndex][0];
15 int index = queries[queryIndex][1];
16
17 // Normalize time to be within one complete cycle (2 * arraySize)
18 // The array goes through a complete cycle of shrinking and growing
19 time %= (arraySize * 2);
20
21 // First half of cycle: array is shrinking from the left
22 // Elements from position 0 to time-1 are removed
23 if (time < arraySize && index < arraySize - time) {
24 // Valid index exists in the shrinking array
25 // Original index shifts by time positions
26 result[queryIndex] = nums[index + time];
27 }
28 // Second half of cycle: array is growing from the left
29 // Array has (time - arraySize) elements added back
30 else if (time > arraySize && index < time - arraySize) {
31 // Valid index exists in the growing array
32 // Elements are added from the beginning
33 result[queryIndex] = nums[index];
34 }
35 }
36
37 return result;
38 }
39};
401/**
2 * Finds elements in a rotating array at specific time points
3 * @param nums - The original array of numbers
4 * @param queries - Array of [time, index] pairs to query
5 * @returns Array of elements at the queried positions, -1 if position is invalid
6 */
7function elementInNums(nums: number[], queries: number[][]): number[] {
8 // Get the length of the input array
9 const arrayLength: number = nums.length;
10
11 // Get the number of queries to process
12 const queryCount: number = queries.length;
13
14 // Initialize result array with -1 for all positions
15 const result: number[] = Array(queryCount).fill(-1);
16
17 // Process each query
18 for (let queryIndex = 0; queryIndex < queryCount; ++queryIndex) {
19 // Destructure time and position from current query
20 let [time, position] = queries[queryIndex];
21
22 // Normalize time to be within one complete cycle (2 * arrayLength)
23 // First half: elements are removed from front
24 // Second half: elements are added to back
25 time %= (2 * arrayLength);
26
27 // First phase: removing elements from the front
28 // Check if position is valid after removing 'time' elements from front
29 if (time < arrayLength && position < arrayLength - time) {
30 // Element at position 'position' after removing 'time' elements
31 result[queryIndex] = nums[position + time];
32 }
33 // Second phase: adding elements back to the array
34 // Check if position is valid when array has 'time - arrayLength' elements
35 else if (time >= arrayLength && position < time - arrayLength) {
36 // Element at position 'position' in the rebuilding array
37 result[queryIndex] = nums[position];
38 }
39 }
40
41 return result;
42}
43Solution Approach
The implementation follows a direct calculation approach based on the cyclic pattern we identified:
-
Initialize the answer array: Create an array
ansof lengthm(number of queries) and initialize all values to-1. This handles the default case where an index is out of bounds. -
Process each query: For each query
(t, i)in thequeriesarray:a. Normalize the time: Calculate
t %= 2 * nto map any time to its position within a single cycle of2nminutes.b. Check removal phase: If
t < n, the array is in the removal phase:- The array currently has
n - telements - These elements start from index
tof the original array - If
i < n - t(index is valid), thenans[j] = nums[i + t] - The mapping
i + tgives us the corresponding index in the original array
c. Check restoration phase: If
t > n, the array is in the restoration phase:- The array currently has
t - nelements - These are the first
t - nelements of the original array - If
i < t - n(index is valid), thenans[j] = nums[i] - No offset is needed since elements are in their original positions
d. Handle empty array: When
t = n, the array is empty, so we keep the default value of-1. - The array currently has
-
Return the result: After processing all queries, return the
ansarray.
The algorithm runs in O(m) time where m is the number of queries, with O(1) time per query. The space complexity is O(m) for storing the answer array.
The elegance of this solution lies in avoiding simulation - we directly compute the state of the array at any given time using mathematical relationships, making it highly efficient even for large time values.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [3, 5, 7] and queries = [[2, 0], [4, 1], [5, 2]].
First, we identify that n = 3, so one complete cycle takes 2n = 6 minutes.
Initial Setup:
- Create answer array:
ans = [-1, -1, -1]
Query 1: [time=2, index=0]
- Normalize time:
t = 2 % 6 = 2 - Since
t = 2 < n = 3, we're in the removal phase - Array has
n - t = 3 - 2 = 1element remaining - Check if
index = 0 < 1: Yes, it's valid - The element at index 0 in current array maps to index
0 + 2 = 2in original array ans[0] = nums[2] = 7- Current array state:
[7](elements at indices 0 and 1 were removed)
Query 2: [time=4, index=1]
- Normalize time:
t = 4 % 6 = 4 - Since
t = 4 > n = 3, we're in the restoration phase - Array has
t - n = 4 - 3 = 1element restored - Check if
index = 1 < 1: No, index 1 is out of bounds ans[1]remains-1- Current array state:
[3](only first element has been restored)
Query 3: [time=5, index=2]
- Normalize time:
t = 5 % 6 = 5 - Since
t = 5 > n = 3, we're in the restoration phase - Array has
t - n = 5 - 3 = 2elements restored - Check if
index = 2 < 2: No, index 2 is out of bounds (we only have indices 0 and 1) ans[2]remains-1- Current array state:
[3, 5](first two elements have been restored)
Final Result: ans = [7, -1, -1]
This example demonstrates how we directly calculate the answer for each query without simulating the entire transformation process, using the mathematical relationships between the current array state and the original array.
Time and Space Complexity
The time complexity is O(m), where m is the length of the queries array. The algorithm iterates through each query exactly once, and for each query, it performs constant-time operations: computing t % (2 * n), comparing values, and potentially accessing an array element. Since the loop runs m times and each iteration takes O(1) time, the overall time complexity is O(m).
The space complexity is O(1) when excluding the space consumed by the answer array ans. The algorithm only uses a fixed amount of extra space for variables n, m, j, t, and i, regardless of the input size. If we include the answer array, the space complexity would be O(m) since we create an array of size m to store the results.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Handle the Empty Array State
A critical pitfall is not properly handling when time_in_cycle == array_length. At this exact moment, the array is completely empty, and any index query should return -1.
Incorrect Implementation:
if time_in_cycle <= array_length: # Wrong! Includes the empty state if target_index < array_length - time_in_cycle: result[query_idx] = nums[target_index + time_in_cycle]
Why it fails: When time_in_cycle == array_length, the expression array_length - time_in_cycle equals 0, but trying to access nums[target_index + array_length] will cause an index out of bounds error.
Correct Implementation:
if time_in_cycle < array_length: # Strict inequality excludes empty state if target_index < array_length - time_in_cycle: result[query_idx] = nums[target_index + time_in_cycle]
2. Incorrect Time Normalization for Negative or Zero-Length Arrays
Another pitfall is not considering edge cases in time normalization or array length.
Problematic Code:
time_in_cycle = time % (2 * array_length) # Fails when array_length is 0
Solution: Add a guard clause for empty input arrays:
if array_length == 0: return [-1] * num_queries time_in_cycle = time % (2 * array_length) if array_length > 0 else 0
3. Off-by-One Errors in Index Mapping
Mixing up the index mapping during the removal phase is common. The key insight is that when t elements have been removed, the current array starts from index t of the original array.
Wrong Mapping:
# Incorrect: forgetting to add the offset if time_in_cycle < array_length: if target_index < array_length - time_in_cycle: result[query_idx] = nums[target_index] # Missing offset!
Correct Mapping:
if time_in_cycle < array_length: if target_index < array_length - time_in_cycle: result[query_idx] = nums[target_index + time_in_cycle] # Correct offset
4. Misunderstanding the Restoration Phase Bounds
During restoration, the array has time_in_cycle - array_length elements, not time_in_cycle elements.
Incorrect Bound Check:
elif time_in_cycle > array_length: if target_index < time_in_cycle: # Wrong bound! result[query_idx] = nums[target_index]
Correct Bound Check:
elif time_in_cycle > array_length: if target_index < time_in_cycle - array_length: # Correct bound result[query_idx] = nums[target_index]
These pitfalls highlight the importance of carefully tracking array bounds and state transitions throughout the cyclic transformation process.
What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!