2899. Last Visited Integers
Problem Description
You are given an integer array nums
where each element is either a positive integer or -1
. Your task is to process this array and find the "last visited integer" for each -1
encountered.
The algorithm works as follows:
-
Initialize two empty arrays:
seen
(to track positive integers) andans
(to store results). -
Iterate through the array
nums
from beginning to end:- When you encounter a positive integer: Add it to the front of the
seen
array (prepend operation). - When you encounter
-1
:- Count how many consecutive
-1
s you've seen so far (including the current one). Let's call this countk
. - If
k ≤ length of seen
: Append thek
-th element fromseen
toans
(1-indexed). - If
k > length of seen
: Append-1
toans
.
- Count how many consecutive
- When you encounter a positive integer: Add it to the front of the
-
Return the
ans
array.
Example walkthrough:
If nums = [5, 2, -1, 3, -1, -1]
:
- Process
5
:seen = [5]
- Process
2
:seen = [2, 5]
(prepend to front) - Process first
-1
:k = 1
, appendseen[1] = 2
toans
- Process
3
:seen = [3, 2, 5]
, reset consecutive count - Process second
-1
:k = 1
, appendseen[1] = 3
toans
- Process third
-1
:k = 2
, appendseen[2] = 2
toans
- Final
ans = [2, 3, 2]
The key insight is that seen
maintains a history of positive integers in reverse order of encounter (most recent first), and consecutive -1
s allow you to look deeper into this history.
Intuition
The problem is essentially asking us to implement a "lookback" mechanism. When we see -1
, we need to retrieve previously seen positive integers, and consecutive -1
s should retrieve progressively older values.
The key insight is recognizing that we need a data structure that maintains the order of positive integers we've seen, with the most recent ones being easily accessible. When we encounter consecutive -1
s, we want to access the 1st most recent, 2nd most recent, 3rd most recent, and so on.
This naturally suggests using a list where we keep track of positive integers in reverse chronological order (most recent first). When we see a positive integer, we add it to the beginning of our tracking list. This way, the first element is always the most recently seen positive integer, the second element is the second most recent, and so forth.
For handling -1
s, we need to track how many consecutive -1
s we've encountered. If it's the first -1
after a positive integer, we want the 1st element from our list. If it's the second consecutive -1
, we want the 2nd element, and so on. This count resets whenever we see a positive integer.
The solution becomes straightforward: maintain a counter k
for consecutive -1
s, and use it as an index into our list of seen positive integers. The index k
directly corresponds to which historical positive integer we should retrieve. If k
exceeds the size of our list, it means we've run out of historical values to look back to, so we return -1
.
This approach elegantly handles the problem's requirements with simple list operations and a single counter variable, making the implementation clean and efficient.
Solution Approach
The implementation follows a straightforward simulation approach based on the problem requirements. Let's break down the solution step by step:
Data Structures Used:
nums
: A list to store all positive integers encountered, maintaining them in the order they appearans
: The result list that stores the answers for each-1
encounteredk
: A counter to track consecutiveprev
(or-1
) occurrences
Algorithm Steps:
-
Initialize variables: Start with empty
nums
andans
lists, and setk = 0
to track consecutiveprev
strings. -
Iterate through the input array
words
:For each element
w
:- If
w == "prev"
(representing-1
in the problem):- Increment
k
by 1 (counting consecutiveprev
s) - Calculate the index:
i = len(nums) - k
- This formula works because:
- If
k = 1
, we want the last element:len(nums) - 1
- If
k = 2
, we want the second-to-last:len(nums) - 2
- And so on...
- If
- If
i < 0
, it meansk
exceeds the size ofnums
, so append-1
toans
- Otherwise, append
nums[i]
toans
- Increment
- If
w
is not"prev"
(it's a positive integer):- Reset
k = 0
(breaking the consecutiveprev
streak) - Convert
w
to integer and append tonums
- Reset
- If
-
Return the
ans
array containing all the results.
Key Insight: Instead of prepending to the front of a seen
array as described in the problem, the solution cleverly appends to nums
and uses index calculation len(nums) - k
to achieve the same effect. This is more efficient as appending is O(1)
while prepending would be O(n)
.
Time Complexity: O(n)
where n
is the length of the input array, as we process each element once.
Space Complexity: O(n)
for storing the nums
and ans
arrays.
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 trace through nums = [1, 2, -1, 1, -1, -1]
to understand how the solution works:
Initial state:
seen = []
(stores positive integers in reverse order)ans = []
(stores results)k = 0
(counts consecutive -1s)
Step 1: Process 1
- It's positive, so prepend to
seen
seen = [1]
,k = 0
(reset)
Step 2: Process 2
- It's positive, so prepend to
seen
seen = [2, 1]
,k = 0
(reset)
Step 3: Process -1
- Increment k:
k = 1
- Look at
seen[1]
(1st element) =2
- Add
2
toans
ans = [2]
Step 4: Process 1
- It's positive, so prepend to
seen
seen = [1, 2, 1]
,k = 0
(reset)
Step 5: Process -1
- Increment k:
k = 1
- Look at
seen[1]
(1st element) =1
- Add
1
toans
ans = [2, 1]
Step 6: Process -1
- Increment k:
k = 2
(2nd consecutive -1) - Look at
seen[2]
(2nd element) =2
- Add
2
toans
ans = [2, 1, 2]
Final Result: [2, 1, 2]
The key insight is that consecutive -1
s act like a "depth counter" into our history of positive integers. The first -1
retrieves the most recent positive integer, the second consecutive -1
retrieves the second most recent, and so on. When a positive integer appears, it resets this depth counter and becomes the new "most recent" value.
Solution Implementation
1class Solution:
2 def lastVisitedIntegers(self, words: List[str]) -> List[int]:
3 # List to store all integer values encountered
4 visited_numbers = []
5 # List to store the result
6 result = []
7 # Counter for consecutive "prev" operations
8 consecutive_prev_count = 0
9
10 # Process each word in the input list
11 for word in words:
12 if word == "prev":
13 # Increment counter for consecutive "prev" operations
14 consecutive_prev_count += 1
15 # Calculate the index to retrieve from visited_numbers
16 # We go back 'consecutive_prev_count' positions from the end
17 target_index = len(visited_numbers) - consecutive_prev_count
18
19 # If index is out of bounds, append -1; otherwise append the number at that index
20 if target_index < 0:
21 result.append(-1)
22 else:
23 result.append(visited_numbers[target_index])
24 else:
25 # Reset the consecutive "prev" counter when we encounter a number
26 consecutive_prev_count = 0
27 # Convert string to integer and add to visited_numbers
28 visited_numbers.append(int(word))
29
30 return result
31
1class Solution {
2 public List<Integer> lastVisitedIntegers(List<String> words) {
3 // List to store all numeric values encountered
4 List<Integer> numbers = new ArrayList<>();
5
6 // List to store the result for each "prev" command
7 List<Integer> result = new ArrayList<>();
8
9 // Counter to track consecutive "prev" commands
10 int consecutivePrevCount = 0;
11
12 // Process each word in the input list
13 for (String word : words) {
14 if ("prev".equals(word)) {
15 // Increment counter for consecutive "prev" commands
16 consecutivePrevCount++;
17
18 // Calculate the index to retrieve from numbers list
19 // The k-th "prev" retrieves the k-th element from the end
20 int targetIndex = numbers.size() - consecutivePrevCount;
21
22 // Add the retrieved value or -1 if index is out of bounds
23 if (targetIndex < 0) {
24 result.add(-1);
25 } else {
26 result.add(numbers.get(targetIndex));
27 }
28 } else {
29 // Reset counter when encountering a number
30 consecutivePrevCount = 0;
31
32 // Convert string to integer and add to numbers list
33 numbers.add(Integer.valueOf(word));
34 }
35 }
36
37 return result;
38 }
39}
40
1class Solution {
2public:
3 vector<int> lastVisitedIntegers(vector<string>& words) {
4 // Store the numeric values encountered in the input
5 vector<int> numbers;
6
7 // Store the result to be returned
8 vector<int> result;
9
10 // Counter to track consecutive "prev" commands
11 int consecutivePrevCount = 0;
12
13 // Process each word in the input array
14 for (const auto& word : words) {
15 if (word == "prev") {
16 // Increment counter for consecutive "prev" commands
17 ++consecutivePrevCount;
18
19 // Calculate the index to access from the end of numbers array
20 // consecutivePrevCount indicates how far back to look
21 int targetIndex = numbers.size() - consecutivePrevCount;
22
23 // If index is valid, add the number at that position; otherwise add -1
24 if (targetIndex < 0) {
25 result.push_back(-1);
26 } else {
27 result.push_back(numbers[targetIndex]);
28 }
29 } else {
30 // Reset the counter when encountering a number
31 consecutivePrevCount = 0;
32
33 // Convert string to integer and add to numbers array
34 numbers.push_back(stoi(word));
35 }
36 }
37
38 return result;
39 }
40};
41
1/**
2 * Processes an array of words containing integers and "prev" commands.
3 * Returns the last visited integers based on "prev" commands.
4 *
5 * @param words - Array of strings containing integers or "prev" command
6 * @returns Array of integers representing the last visited values
7 */
8function lastVisitedIntegers(words: string[]): number[] {
9 // Store all numeric values encountered
10 const numericValues: number[] = [];
11
12 // Store the result for each "prev" command
13 const result: number[] = [];
14
15 // Track consecutive "prev" commands count
16 let consecutivePrevCount: number = 0;
17
18 // Process each word in the input array
19 for (const word of words) {
20 if (word === 'prev') {
21 // Increment the count of consecutive "prev" commands
22 consecutivePrevCount++;
23
24 // Calculate the index to retrieve from numericValues
25 // The k-th "prev" retrieves the k-th last element
26 const targetIndex: number = numericValues.length - consecutivePrevCount;
27
28 // Add the value at targetIndex or -1 if index is out of bounds
29 result.push(targetIndex < 0 ? -1 : numericValues[targetIndex]);
30 } else {
31 // Reset consecutive "prev" count when encountering a number
32 consecutivePrevCount = 0;
33
34 // Convert string to number and add to numericValues
35 numericValues.push(Number(word));
36 }
37 }
38
39 return result;
40}
41
Time and Space Complexity
Time Complexity: O(n)
, where n
is the length of the array words
.
The algorithm iterates through the words
array exactly once using a single for loop. Within each iteration, all operations are constant time:
- Checking if
w == "prev"
takesO(1)
- Appending to lists takes
O(1)
amortized time - Accessing an element by index
nums[i]
takesO(1)
- Converting string to integer takes
O(1)
for reasonable integer sizes
Since we perform O(1)
operations for each of the n
elements in words
, the total time complexity is O(n)
.
Space Complexity: O(n)
, where n
is the length of the array words
.
The space usage comes from:
nums
list: In the worst case where all elements are numeric strings, this stores up ton
integers, requiringO(n)
spaceans
list: In the worst case where all elements are "prev", this stores up ton
values, requiringO(n)
space- Other variables (
k
,i
,w
) use constantO(1)
space
The total auxiliary space used is O(n) + O(n) + O(1) = O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Index Calculation
One of the most common mistakes is incorrectly calculating which element to retrieve when encountering consecutive -1
s (or "prev" operations).
Pitfall Example:
# INCORRECT: Using k-1 as index
target_index = k - 1
if target_index < len(visited_numbers):
result.append(visited_numbers[target_index])
Why it's wrong: The consecutive count k
represents how many steps back we need to go from the end of the array, not a direct index. With k=1
, we want the last element (index len-1
), not the first element (index 0
).
Correct Solution:
# CORRECT: Calculate from the end of the array
target_index = len(visited_numbers) - consecutive_prev_count
if target_index >= 0:
result.append(visited_numbers[target_index])
2. Not Resetting the Consecutive Counter
Forgetting to reset the consecutive counter when encountering a positive integer leads to incorrect lookback distances.
Pitfall Example:
for word in words:
if word == "prev":
consecutive_prev_count += 1
# ... handle prev logic
else:
# FORGOT to reset consecutive_prev_count!
visited_numbers.append(int(word))
Why it's wrong: If we don't reset the counter, the next "prev" will incorrectly think it's part of a longer consecutive sequence, leading to looking too far back in the history.
Correct Solution:
else:
consecutive_prev_count = 0 # MUST reset the counter
visited_numbers.append(int(word))
3. Off-by-One Error in Boundary Check
Using the wrong comparison operator when checking if the index is valid.
Pitfall Example:
# INCORRECT: Using <= instead of < if target_index <= 0: # Wrong condition result.append(-1) else: result.append(visited_numbers[target_index])
Why it's wrong: When target_index = 0
, it's actually a valid index pointing to the first element. Using <= 0
would incorrectly return -1
instead of visited_numbers[0]
.
Correct Solution:
# CORRECT: Check if index is negative if target_index < 0: result.append(-1) else: result.append(visited_numbers[target_index])
4. Attempting to Prepend Instead of Append
Trying to literally implement the "prepend to front" operation as described in the problem statement.
Pitfall Example:
# INEFFICIENT: Prepending to list (O(n) operation)
visited_numbers.insert(0, int(word))
# Then using direct indexing
if k <= len(visited_numbers):
result.append(visited_numbers[k-1])
Why it's problematic: While this works logically, insert(0, ...)
is an O(n) operation in Python lists, making the overall solution O(n²) instead of O(n).
Better Solution:
# EFFICIENT: Append and calculate index from the end
visited_numbers.append(int(word))
target_index = len(visited_numbers) - consecutive_prev_count
This approach maintains O(1) append operations while achieving the same logical result through index calculation.
Which of the following shows the order of node visit in a Breadth-first Search?
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!