Skip to content

900. RLE Iterator

On LeetCode ->

Problem

Design an iterator over [count, value, ...]. Each next(n) consumes n elements and returns the last consumed value, or -1 if fewer than n remain. A failed call still exhausts the remaining elements.

encoding = [3, 8, 0, 9, 2, 5]  →  [8, 8, 8, 5, 5]

Call       Return   Remaining
next(2)       8     [8, 5, 5]
next(1)       8     [5, 5]
next(1)       5     [5]
next(2)      -1     []
class RLEIterator:
    def __init__(self, encoding: list[int]):
        pass

    def next(self, n: int) -> int:
        pass

Key trick

Track the current run and how much of it has been consumed; skip entire runs rather than individual elements.

Trap

Zero-length runs must be skipped, consuming exactly a run's remaining count must return its value, and an oversized request must leave the iterator exhausted.

Why is it interesting?

It tests stateful API design and amortized analysis: large counts are inexpensive because each run is passed at most once.

Python solution

class RLEIterator:
    def __init__(self, encoding: list[int]):
        self.encoding = encoding
        self.i = 0
        self.used = 0

    def next(self, n: int) -> int:
        while self.i < len(self.encoding):
            rem = self.encoding[self.i] - self.used

            if n <= rem:
                # Equality still returns this run's value.
                self.used += n
                return self.encoding[self.i + 1]

            # Consume this run, including zero-length runs.
            n -= rem
            self.i += 2
            self.used = 0

        return -1

For \(k\) encoded runs and \(q\) calls:

  • Time: \(O(k)\) worst case per call; \(O(k + q)\) across all calls.
  • Auxiliary space: \(O(1)\); the input is neither copied nor expanded.

Comment on my solution

Your solution is correct, including zero-length runs, exact boundaries, and exhaustion after oversized requests. It has the same complexity as the solution above and does not modify the input.

The main simplification is removing cached val and _hasnext(): the current value is available directly from the encoding. _hasnext() can return True when only zero-length runs remain, but your loop handles that correctly.

# WORKS
class RLEIterator:
    def __init__(self, encoding: list[int]):
        self.encoding = encoding
        self.i = 0
        self.cnt = 0
        self.val = None

    def _hasnext(self):
        return self.cnt > 0 or self.i < len(self.encoding)

    def next(self, n: int) -> int:
        while n > 0 and self._hasnext():
            if self.cnt >= n:
                self.cnt -= n
                return self.val
            else:
                n -= self.cnt
                self.cnt = 0
                self.val = None
            if self.i < len(self.encoding):
                self.cnt = self.encoding[self.i]
                self.val = self.encoding[self.i + 1]
                self.i += 2
        return -1