Facebook Pixel

2899. Last Visited Integers

EasyArraySimulation
Leetcode Link

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:

  1. Initialize two empty arrays: seen (to track positive integers) and ans (to store results).

  2. 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 -1s you've seen so far (including the current one). Let's call this count k.
      • If k ≤ length of seen: Append the k-th element from seen to ans (1-indexed).
      • If k > length of seen: Append -1 to ans.
  3. 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, append seen[1] = 2 to ans
  • Process 3: seen = [3, 2, 5], reset consecutive count
  • Process second -1: k = 1, append seen[1] = 3 to ans
  • Process third -1: k = 2, append seen[2] = 2 to ans
  • 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 -1s allow you to look deeper into this history.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 -1s 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 -1s, 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 -1s, we need to track how many consecutive -1s 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 -1s, 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 appear
  • ans: The result list that stores the answers for each -1 encountered
  • k: A counter to track consecutive prev (or -1) occurrences

Algorithm Steps:

  1. Initialize variables: Start with empty nums and ans lists, and set k = 0 to track consecutive prev strings.

  2. Iterate through the input array words:

    For each element w:

    • If w == "prev" (representing -1 in the problem):
      • Increment k by 1 (counting consecutive prevs)
      • 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 i < 0, it means k exceeds the size of nums, so append -1 to ans
      • Otherwise, append nums[i] to ans
    • If w is not "prev" (it's a positive integer):
      • Reset k = 0 (breaking the consecutive prev streak)
      • Convert w to integer and append to nums
  3. 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 Evaluator

Example 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 to ans
  • 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 to ans
  • ans = [2, 1]

Step 6: Process -1

  • Increment k: k = 2 (2nd consecutive -1)
  • Look at seen[2] (2nd element) = 2
  • Add 2 to ans
  • ans = [2, 1, 2]

Final Result: [2, 1, 2]

The key insight is that consecutive -1s 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" takes O(1)
  • Appending to lists takes O(1) amortized time
  • Accessing an element by index nums[i] takes O(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 to n integers, requiring O(n) space
  • ans list: In the worst case where all elements are "prev", this stores up to n values, requiring O(n) space
  • Other variables (k, i, w) use constant O(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 -1s (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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following shows the order of node visit in a Breadth-first Search?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More