3237. Alt and Tab Simulation 🔒
Problem Description
You have n
windows open, numbered from 1
to n
. These windows are arranged in a stack-like order where you can navigate between them using alt + tab functionality.
You're given two arrays:
windows
: Contains the initial order of windows from top to bottom (first element is at the top, last element is at the bottom)queries
: Contains a sequence of window numbers that will be brought to the top one by one
When a window in queries[i]
is brought to the top, it moves from its current position to become the topmost window, while all other windows maintain their relative order.
Your task is to simulate this process and return the final arrangement of windows after all queries have been processed.
For example, if you have windows [1, 2, 3]
and queries [3, 2]
:
- After query
3
: Window 3 moves to top →[3, 1, 2]
- After query
2
: Window 2 moves to top →[2, 3, 1]
The solution cleverly processes queries in reverse order. Since the last query determines which window ends up on top, by traversing backwards and using a hash table to track already-placed windows, we can build the final arrangement efficiently. Windows from queries that appear later are placed first (as they should be on top), followed by any remaining windows from the original arrangement that weren't queried.
Intuition
The key insight comes from recognizing that the final position of a window depends on when it was last queried. If we process queries in the normal forward order, we'd need to constantly rearrange windows, which is inefficient.
Think about it this way: if a window appears multiple times in queries, only its last appearance matters for its final position. For instance, if window 2
is queried at positions i
and j
where i < j
, the query at position i
becomes irrelevant because the query at position j
will override it by bringing window 2
to the top again.
This observation leads us to process queries in reverse order. When traversing backwards:
- The first time we encounter a window in queries (which is actually its last query in the original order), we know this determines its final position
- Windows encountered earlier in reverse traversal should appear higher in the final result since they were queried later in the original sequence
By using a hash set s
to track windows we've already placed, we can:
- Add each uniquely queried window to our answer in the order we encounter them (reverse of queries)
- Skip duplicate occurrences since we've already determined that window's final position
- Finally, append any windows from the original arrangement that were never queried, maintaining their relative order
This approach elegantly handles the "most recent query wins" nature of the problem without actually simulating each individual window movement, achieving O(m + n)
time complexity where m
is the length of queries and n
is the number of windows.
Solution Approach
The solution implements the reverse traversal strategy with a hash table to efficiently build the final window arrangement.
Step-by-step implementation:
-
Initialize data structures:
- Create an empty set
s
to track windows that have already been placed in the result - Create an empty list
ans
to build the final window arrangement
- Create an empty set
-
Process queries in reverse order:
for q in queries[::-1]: if q not in s: ans.append(q) s.add(q)
- Use
queries[::-1]
to traverse the queries array from end to beginning - For each query
q
, check if it's already in the sets
- If not present (first occurrence in reverse, which means last occurrence in original order):
- Add
q
to the answer arrayans
- Add
q
to the sets
to mark it as processed
- Add
- Use
-
Add remaining windows:
for w in windows: if w not in s: ans.append(w)
- Iterate through the original
windows
array - Add any window
w
that hasn't been queried (not in sets
) to the answer - These windows maintain their relative order from the original arrangement
- Iterate through the original
-
Return the result:
- The
ans
array now contains the final window arrangement
- The
Time Complexity: O(m + n)
where m
is the length of queries and n
is the number of windows
- Reversing queries:
O(m)
- Processing queries:
O(m)
withO(1)
hash set operations - Processing remaining windows:
O(n)
Space Complexity: O(n)
for the hash set storing at most n
unique windows
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 a concrete example to illustrate the solution approach.
Given:
windows = [3, 1, 4, 2]
(initial order from top to bottom)queries = [4, 1, 4, 3]
(sequence of windows to bring to top)
Step 1: Initialize data structures
s = {}
(empty set to track placed windows)ans = []
(empty list for final arrangement)
Step 2: Process queries in reverse order
We traverse queries
backwards: [3, 4, 1, 4]
-
Query 3: Not in
s
- Add 3 to
ans
:ans = [3]
- Add 3 to
s
:s = {3}
- Add 3 to
-
Query 4: Not in
s
- Add 4 to
ans
:ans = [3, 4]
- Add 4 to
s
:s = {3, 4}
- Add 4 to
-
Query 1: Not in
s
- Add 1 to
ans
:ans = [3, 4, 1]
- Add 1 to
s
:s = {3, 4, 1}
- Add 1 to
-
Query 4: Already in
s
(skip)- No changes to
ans
ors
- No changes to
Step 3: Add remaining windows from original arrangement
Check each window in windows = [3, 1, 4, 2]
:
- Window 3: In
s
(skip) - Window 1: In
s
(skip) - Window 4: In
s
(skip) - Window 2: Not in
s
- Add 2 to
ans
:ans = [3, 4, 1, 2]
- Add 2 to
Final Result: [3, 4, 1, 2]
Verification: Let's verify by simulating the original queries:
- Initial:
[3, 1, 4, 2]
- Query 4: Move 4 to top →
[4, 3, 1, 2]
- Query 1: Move 1 to top →
[1, 4, 3, 2]
- Query 4: Move 4 to top →
[4, 1, 3, 2]
- Query 3: Move 3 to top →
[3, 4, 1, 2]
✓
The reverse processing correctly produces the same result without simulating each move!
Solution Implementation
1class Solution:
2 def simulationResult(self, windows: List[int], queries: List[int]) -> List[int]:
3 # Set to track which window IDs have been processed
4 processed_windows = set()
5
6 # Result list to store the final window order
7 result = []
8
9 # Process queries in reverse order (most recent first)
10 # This simulates bringing queried windows to the front
11 for query_window in reversed(queries):
12 # Only add window if it hasn't been processed yet
13 if query_window not in processed_windows:
14 result.append(query_window)
15 processed_windows.add(query_window)
16
17 # Add remaining windows that weren't queried
18 # These maintain their original relative order
19 for window in windows:
20 if window not in processed_windows:
21 result.append(window)
22
23 return result
24
1class Solution {
2 /**
3 * Simulates a result based on windows and queries arrays.
4 * Processes queries in reverse order to maintain uniqueness,
5 * then appends remaining windows that weren't in queries.
6 *
7 * @param windows Array of window values
8 * @param queries Array of query values to process
9 * @return Resulting array with unique values from queries (reversed) followed by remaining windows
10 */
11 public int[] simulationResult(int[] windows, int[] queries) {
12 int windowsLength = windows.length;
13
14 // Boolean array to track which values have been used (1-indexed based on window values)
15 boolean[] isUsed = new boolean[windowsLength + 1];
16
17 // Result array to store the final answer
18 int[] result = new int[windowsLength];
19
20 // Index pointer for filling the result array
21 int resultIndex = 0;
22
23 // Process queries in reverse order
24 for (int i = queries.length - 1; i >= 0; i--) {
25 int currentQuery = queries[i];
26
27 // Add query value to result only if it hasn't been used before
28 if (!isUsed[currentQuery]) {
29 result[resultIndex] = currentQuery;
30 resultIndex++;
31 isUsed[currentQuery] = true;
32 }
33 }
34
35 // Add remaining window values that weren't present in queries
36 for (int windowValue : windows) {
37 if (!isUsed[windowValue]) {
38 result[resultIndex] = windowValue;
39 resultIndex++;
40 }
41 }
42
43 return result;
44 }
45}
46
1class Solution {
2public:
3 vector<int> simulationResult(vector<int>& windows, vector<int>& queries) {
4 int windowCount = windows.size();
5
6 // Track which windows have been selected (1-indexed)
7 vector<bool> isSelected(windowCount + 1, false);
8
9 // Store the final result
10 vector<int> result;
11
12 // Process queries in reverse order
13 // The condition ~i checks if i is not -1 (equivalent to i >= 0)
14 for (int i = queries.size() - 1; i >= 0; --i) {
15 int currentQuery = queries[i];
16
17 // If this window hasn't been selected yet, add it to result
18 if (!isSelected[currentQuery]) {
19 isSelected[currentQuery] = true;
20 result.push_back(currentQuery);
21 }
22 }
23
24 // Add remaining windows that weren't selected by queries
25 // These are added in their original order
26 for (int window : windows) {
27 if (!isSelected[window]) {
28 result.push_back(window);
29 }
30 }
31
32 return result;
33 }
34};
35
1/**
2 * Simulates a window selection process based on queries
3 * @param windows - Array of window IDs available initially
4 * @param queries - Array of window IDs to be queried/selected in order
5 * @returns Array representing the final order of windows after processing queries
6 */
7function simulationResult(windows: number[], queries: number[]): number[] {
8 // Get the total number of windows
9 const windowCount: number = windows.length;
10
11 // Track which windows have been selected (indexed by window ID)
12 // Array size is windowCount + 1 to handle 1-based indexing
13 const isSelected: boolean[] = Array(windowCount + 1).fill(false);
14
15 // Result array to store the final window order
16 const result: number[] = [];
17
18 // Process queries in reverse order to handle most recent selections first
19 for (let index = queries.length - 1; index >= 0; index--) {
20 const currentQuery: number = queries[index];
21
22 // If this window hasn't been selected yet, mark it and add to result
23 if (!isSelected[currentQuery]) {
24 isSelected[currentQuery] = true;
25 result.push(currentQuery);
26 }
27 }
28
29 // Add remaining windows that weren't queried, maintaining original order
30 for (const windowId of windows) {
31 if (!isSelected[windowId]) {
32 result.push(windowId);
33 }
34 }
35
36 return result;
37}
38
Time and Space Complexity
The time complexity is O(n + m)
, where n
is the length of the windows
array and m
is the length of the queries
array.
Breaking down the time complexity:
- Reversing the
queries
array withqueries[::-1]
takesO(m)
time - The first loop iterates through all reversed queries, performing
O(1)
operations (set lookup, list append, set add) for each element, takingO(m)
time total - The second loop iterates through all windows, performing
O(1)
operations (set lookup, list append) for each element, takingO(n)
time total - Overall:
O(m) + O(m) + O(n) = O(n + m)
The space complexity is O(m)
, where m
is the length of the queries
array.
Breaking down the space complexity:
- The set
s
stores at mostm
unique elements from queries (in the worst case, all queries are unique), requiringO(m)
space - The
ans
list stores at mostn + m
elements, but this is considered the output and typically not counted in space complexity analysis - The reversed queries
queries[::-1]
creates a new list of sizem
, requiringO(m)
space - Overall auxiliary space:
O(m)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Processing Duplicate Queries Incorrectly
The Pitfall: A common mistake is not handling duplicate window IDs in the queries array properly. If you don't use a set to track already-processed windows, you might add the same window multiple times to the result.
Incorrect Implementation:
def simulationResult(self, windows: List[int], queries: List[int]) -> List[int]:
result = []
# Wrong: No tracking of processed windows
for query_window in reversed(queries):
result.append(query_window) # Adds duplicates!
for window in windows:
if window not in result: # O(n) operation each time!
result.append(window)
return result
Why it fails: If queries = [1, 2, 1]
, the reversed order [1, 2, 1]
would add window 1 twice to the result, creating an invalid arrangement.
The Fix: Always use a set to track which windows have been processed:
processed_windows = set()
for query_window in reversed(queries):
if query_window not in processed_windows: # Check before adding
result.append(query_window)
processed_windows.add(query_window)
2. Using List Membership Check Instead of Set
The Pitfall: Using if window not in result
instead of a separate set for tracking, which degrades performance.
Inefficient Implementation:
def simulationResult(self, windows: List[int], queries: List[int]) -> List[int]:
result = []
for query_window in reversed(queries):
if query_window not in result: # O(n) operation!
result.append(query_window)
for window in windows:
if window not in result: # O(n) operation!
result.append(window)
return result
Why it's problematic: List membership checking (in
operator) has O(n) time complexity, making the overall solution O(n²) instead of O(n).
The Fix: Use a separate set for O(1) membership checking:
processed_windows = set() # O(1) membership checking
3. Forgetting to Maintain Original Order for Non-Queried Windows
The Pitfall: Adding non-queried windows in the wrong order or using a data structure that doesn't preserve the original ordering.
Incorrect Implementation:
def simulationResult(self, windows: List[int], queries: List[int]) -> List[int]:
result = []
processed_windows = set()
for query_window in reversed(queries):
if query_window not in processed_windows:
result.append(query_window)
processed_windows.add(query_window)
# Wrong: Using set difference loses original order!
remaining = set(windows) - processed_windows
result.extend(remaining) # Order is not guaranteed
return result
Why it fails: Sets don't maintain insertion order (in Python < 3.7) or the original order from the windows array, so the relative positions of non-queried windows would be lost.
The Fix: Iterate through the original windows array to preserve order:
for window in windows: # Maintains original order if window not in processed_windows: result.append(window)
In a binary min heap, the maximum element can be found in:
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 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
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!