class PeekingIterator:
def __init__(self, iterator):
self.iterator = iterator
self._has_next = True
try:
self._next = next(self.iterator)
except StopIteration:
self._has_next = False
self._next = None
def peek(self):
return self._next
def next(self):
if not self._has_next:
raise StopIteration()
curr = self._next
try:
self._next = next(self.iterator)
except StopIteration:
self._has_next = False
self._next = None
return curr
def hasNext(self):
return self._has_next
class PeekingIterator : public Iterator {
int nextVal;
bool has_next;
public:
PeekingIterator(const vector<int>& nums) : Iterator(nums) {
if (Iterator::hasNext()) {
nextVal = Iterator::next();
has_next = true;
} else {
has_next = false;
}
}
int peek() {
return nextVal;
}
int next() {
int curr = nextVal;
if (Iterator::hasNext()) {
nextVal = Iterator::next();
} else {
has_next = false;
}
return curr;
}
bool hasNext() const {
return has_next;
}
};
class PeekingIterator implements Iterator<Integer> {
private Iterator<Integer> iterator;
private Integer nextVal;
private boolean hasNext;
public PeekingIterator(Iterator<Integer> iterator) {
this.iterator = iterator;
if (iterator.hasNext()) {
nextVal = iterator.next();
hasNext = true;
} else {
hasNext = false;
}
}
public Integer peek() {
return nextVal;
}
@Override
public Integer next() {
Integer curr = nextVal;
if (iterator.hasNext()) {
nextVal = iterator.next();
} else {
hasNext = false;
}
return curr;
}
@Override
public boolean hasNext() {
return hasNext;
}
}
var PeekingIterator = function(iterator) {
this.iterator = iterator;
this._hasNext = iterator.hasNext();
this._next = this._hasNext ? iterator.next() : null;
};
PeekingIterator.prototype.peek = function() {
return this._next;
};
PeekingIterator.prototype.next = function() {
let curr = this._next;
this._hasNext = this.iterator.hasNext();
this._next = this._hasNext ? this.iterator.next() : null;
return curr;
};
PeekingIterator.prototype.hasNext = function() {
return this._hasNext;
};
The Peeking Iterator problem asks you to design an iterator class that supports not only the standard next()
and hasNext()
operations, but also a peek()
operation. The peek()
method should return the next element in the iteration without advancing the iterator.
PeekingIterator
that wraps this iterator and supports the following methods:
peek()
: Returns the next element in the iterator without advancing it.next()
: Returns the next element and advances the iterator.hasNext()
: Returns whether the iterator has more elements.
At first glance, the problem seems straightforward: just call next()
on the underlying iterator when needed. However, the challenge is with the peek()
operation, which requires us to look at the next element without moving the iterator forward.
If we call next()
to get the value for peek()
, we have already advanced the iterator, which is not allowed. We need a way to "see" the next value and remember it for both peek()
and next()
calls. This is a classic "look ahead" problem, which can be solved by caching the next value.
The brute-force idea would be to reconstruct the iterator or underlying list each time, but this is inefficient and breaks the O(1) time requirement. Instead, we can store the next value in a variable whenever we advance, and use that for both peek()
and next()
.
PeekingIterator
is created, immediately advance the underlying iterator once and store the value in a variable (let's call it _next
)._has_next
) to indicate whether there are more elements.peek()
_next
value.next()
_next
value._next
and _has_next
.hasNext()
_has_next
flag.
This approach ensures that all operations are O(1) and that peek()
never modifies the iterator state.
Suppose the input iterator is over the list [1, 2, 3]
.
next()
on the underlying iterator and store 1
in _next
._has_next
is True
(since there are more elements).peek()
call:
1
(value of _next
).next()
call:
1
.2
in _next
.peek()
call:
2
.next()
call:
2
.3
in _next
.peek()
call:
3
.next()
call:
3
._has_next
to False
.hasNext()
calls:
False
.
At no point does peek()
advance the iterator; only next()
does.
peek()
, each operation could be O(n), which is too slow.peek()
, next()
, hasNext()
) run in O(1) time because they only access or update a cached value and a flag.The Peeking Iterator problem is a classic example of using a "lookahead" buffer to extend the functionality of an iterator. By caching the next value and maintaining a simple flag, we can efficiently support peeking at the next element without advancing, while keeping all operations at constant time and space. This approach is elegant, efficient, and demonstrates the power of careful state management in iterator design.