LeetCode 251: Flatten 2D Vector Solution in Python – A Step-by-Step Guide

Imagine you’ve got a grid of numbers—like rows of boxes—and you need to pull out each number one by one as if it were a single, long line. That’s the challenge of LeetCode 251: Flatten 2D Vector! This medium-level problem asks you to design a class that flattens a 2D list into a 1D sequence, allowing you to iterate over it with next() and check if there’s more with hasNext(). Using Python, we’ll explore two solutions: the Best Solution, an iterator with two pointers, and an Alternative Solution, a pre-flattened list approach. With detailed examples, clear code, and beginner-friendly breakdowns—especially for the best solution—this guide will help you master iterators and level up your coding skills. Let’s flatten that grid and start iterating!

What Is LeetCode 251: Flatten 2D Vector?

In LeetCode 251: Flatten 2D Vector, you’re tasked with implementing a Vector2D class that takes a 2D list of integers and provides two methods: next() to return the next element, and hasNext() to check if more elements remain. This problem is a practical twist on array manipulation, related to challenges like LeetCode 238: Product of Array Except Self, but focuses on sequential access rather than computation.

Problem Statement

  • Constructor: Vector2D(vector)—initialize with a 2D list.
  • Method: next()—return the next integer in the flattened sequence.
  • Method: hasNext()—return true if more elements exist, false otherwise.
  • Rules: Process elements row by row, left to right; handle empty rows.

Constraints

  • Vector size: 0 to 10^5 total elements.
  • Row length: Variable, may be empty.
  • Values: -10^4 to 10^4.

Examples

  • Input: vector = [[1,2], [3], [4]]
    • Calls:
      • next()1
      • next()2
      • hasNext()true
      • next()3
      • next()4
      • hasNext()false
  • Input: vector = [[]]
    • hasNext()false

Understanding the Problem: Flattening a Grid

To solve LeetCode 251: Flatten 2D Vector in Python, we need to design a class that turns a 2D list (rows and columns) into a 1D sequence we can step through one element at a time. A naive approach—flattening the entire list upfront—works but uses extra space. We’ll explore two methods: 1. Best Solution (Iterator with Two Pointers): O(1) space—efficient and dynamic. 2. Alternative Solution (Pre-Flattened List): O(n) space—simple but memory-heavy.

Let’s dive into the best solution with extra detail for beginners.

Best Solution: Iterator with Two Pointers

Why This Is the Best Solution

The iterator with two pointers approach is the top choice for LeetCode 251 because it flattens the 2D vector on-the-fly using O(1) extra space, only tracking our position with two pointers. It’s memory-efficient, handles empty rows gracefully, and runs in O(1) time per call, making it ideal for this problem.

How It Works

Think of this solution as walking through a 2D grid with two fingers: one finger points to the current row, and the other points to the current column within that row. You move step-by-step, grabbing the next number and sliding your fingers along, skipping empty rows as needed. Here’s how it works, explained simply:

  • Setup:
    • Use row to track the current row index (starts at 0).
    • Use col to track the current column index within that row (starts at 0).
    • Store the 2D list as self.vector.
  • hasNext():
    • Check if we’re still within bounds (row < total rows).
    • If the current row is exhausted (col ≥ row length), move to the next non-empty row.
    • Return true if a next element exists, false otherwise.
  • next():
    • Get the current element at vector[row][col].
    • Move col forward by 1.
    • If col reaches the end of the row, move row to the next non-empty row and reset col to 0.
    • Return the element.
  • Skipping Empty Rows: After moving past a row, skip any empty rows by incrementing row until you find a non-empty one or run out of rows.

It’s like reading a book page by page, line by line, skipping blank pages!

Step-by-Step Example

Example: vector = [[1,2], [3], [4]]

  • Initialization: row = 0, col = 0, vector = [[1,2], [3], [4]].
  • Call 1: hasNext():
    • row = 0 < 3 (len(vector)), col = 0 < 2 (len(row 0)).
    • Element exists → true.
  • Call 2: next():
    • Get vector[0][0] = 1.
    • col = 1.
    • Return 1.
  • Call 3: next():
    • Get vector[0][1] = 2.
    • col = 2 ≥ 2 (row length), so row = 1, col = 0.
    • Return 2.
  • Call 4: hasNext():
    • row = 1 < 3, col = 0 < 1 (len(row 1)).
    • true.
  • Call 5: next():
    • Get vector[1][0] = 3.
    • col = 1 ≥ 1, so row = 2, col = 0.
    • Return 3.
  • Call 6: next():
    • Get vector[2][0] = 4.
    • col = 1 ≥ 1, row = 3 ≥ 3 (len(vector)).
    • Return 4.
  • Call 7: hasNext():
    • row = 3 ≥ 3, no more rows → false.

Code with Detailed Line-by-Line Explanation

Here’s the Python code, explained for beginners:

class Vector2D:
    def __init__(self, vector: List[List[int]]):
        # Step 1: Store the grid and set starting points
        self.vector = vector
        self.row = 0  # Which row we’re on
        self.col = 0  # Which column in that row

        # Step 2: Skip initial empty rows
        while self.row < len(self.vector) and not self.vector[self.row]:
            self.row += 1

    def next(self) -> int:
        # Step 3: Get the current number
        value = self.vector[self.row][self.col]

        # Step 4: Move to next column
        self.col += 1

        # Step 5: If row is done, move to next non-empty row
        if self.col >= len(self.vector[self.row]):
            self.row += 1
            self.col = 0
            # Skip empty rows
            while self.row < len(self.vector) and not self.vector[self.row]:
                self.row += 1

        # Step 6: Return the number we found
        return value

    def hasNext(self) -> bool:
        # Step 7: Check if we’re still in bounds
        if self.row >= len(self.vector):
            return False

        # Step 8: If current row is exhausted, move to next non-empty
        while self.row < len(self.vector) and self.col >= len(self.vector[self.row]):
            self.row += 1
            self.col = 0

        # Step 9: Are there more elements?
        return self.row < len(self.vector)
  • Time Complexity: O(1) amortized per call—each element visited once over all calls.
  • Space Complexity: O(1)—just two pointers.

This solution is like a nimble guide walking you through the grid—light and fast!

Alternative Solution: Pre-Flattened List

Why an Alternative Approach?

The pre-flattened list approach flattens the 2D vector into a 1D list during initialization, then iterates over it. It’s simpler to understand but uses O(n) space, making it a good starting point for beginners who want a straightforward solution.

How It Works

  • Constructor: Flatten the 2D list into a 1D list.
  • next(): Return the next element and move the pointer.
  • hasNext(): Check if the pointer is within bounds.

Step-by-Step Example

Example: vector = [[1,2], [3], [4]]

  • Initialization: Flatten to [1, 2, 3, 4], index = 0.
  • next(): list[0] = 1, index = 1, return 1.
  • next(): list[1] = 2, index = 2, return 2.
  • hasNext(): index = 2 < 4, return true.
  • next(): list[2] = 3, index = 3, return 3.
  • next(): list[3] = 4, index = 4, return 4.
  • hasNext(): index = 4 ≥ 4, return false.

Code for Pre-Flattened Approach

class Vector2D:
    def __init__(self, vector: List[List[int]]):
        # Step 1: Flatten the 2D list
        self.flat = []
        for row in vector:
            self.flat.extend(row)
        self.index = 0  # Where we are in the flat list

    def next(self) -> int:
        # Step 2: Get the next number
        value = self.flat[self.index]
        self.index += 1
        return value

    def hasNext(self) -> bool:
        # Step 3: Check if more numbers exist
        return self.index < len(self.flat)
  • Time Complexity: O(n) for initialization, O(1) per call.
  • Space Complexity: O(n)—storing the flattened list.

It’s easy but memory-intensive.

Comparing the Two Solutions

  • Best Solution (Two Pointers):
    • Pros: O(1) space, dynamic, efficient.
    • Cons: Slightly trickier with pointers.
  • Alternative Solution (Pre-Flattened):
    • Pros: Simple, intuitive.
    • Cons: O(n) space, less scalable.

Two-pointer wins for efficiency.

Additional Examples and Edge Cases

Empty Vector

  • [[]]hasNext() returns false.

Single Row

  • [[1,2,3]]1, 2, 3.

Mixed Empty Rows

  • [[1], [], [2]]1, 2.

Both handle these well.

Complexity Breakdown

  • Two Pointers:
    • Time: O(1) per call, O(n) total.
    • Space: O(1)—pointers only.
  • Pre-Flattened:
    • Time: O(n) init, O(1) per call.
    • Space: O(n)—flat list.

Two-pointer is leaner.

Key Takeaways for Beginners

  • Two Pointers: Track position without flattening.
  • Iterators: Step through data dynamically.
  • Pre-Flattening: Easy but costly in space.
  • Python Tip: Lists and indices are powerful—see [Python Basics](/python/basics).

Final Thoughts: Flatten Like a Pro

LeetCode 251: Flatten 2D Vector in Python is a practical iterator challenge. The two-pointer solution offers O(1) space brilliance, while pre-flattening provides simplicity. Want more? Try LeetCode 341: Flatten Nested List Iterator or LeetCode 284: Peeking Iterator. Ready to iterate? Head to Solve LeetCode 251 on LeetCode and flatten that vector today!