Article / 文章
LeetCode 第284题:顶端迭代器
给定一个迭代器类的接口,接口包含两个方法: next() 和 hasNext()。设计并实现一个支持 peek() 操作的顶端迭代器 -- 其本质就是把原本应由 next() 方法返回的元素 peek() 出来。 实现 PeekingIterator 类: - PeekingIterator(Iterator iter) 使用指定的整数迭代器 iter 初始
📖 文章摘要
本文详细解析LeetCode第284题“顶端迭代器”,这是一道考察迭代器设计的中等难度题目。文章提供了缓存和装饰器两种实现方案,包含C#、Python、C++三种语言实现,配有详细的迭代器状态分析和性能对比。适合学习迭代器设计模式和高级数据结构的读者。
核心知识点: 迭代器设计、缓存机制、设计模式
难度等级: 中等
推荐人群: 具备基础数据结构知识,想要提升设计模式应用能力的开发者
题目描述
给定一个迭代器类的接口,接口包含两个方法: next() 和 hasNext()。设计并实现一个支持 peek() 操作的顶端迭代器 – 其本质就是把原本应由 next() 方法返回的元素 peek() 出来。
实现 PeekingIterator 类:
PeekingIterator(Iterator<int> iter)使用指定的整数迭代器iter初始化迭代器。int peek()返回下一个元素,而不移动指针。int next()返回下一个元素并将指针移动一步。boolean hasNext()如果数据流中存在剩余元素,返回true;否则返回false。
示例
示例 1:
输入:
["PeekingIterator", "next", "peek", "next", "next", "hasNext"]
[[[1, 2, 3]], [], [], [], [], []]
输出:
[null, 1, 2, 2, 3, false]
解释:
PeekingIterator peekingIterator = new PeekingIterator([1, 2, 3]); // [1,2,3]
peekingIterator.next(); // 返回 1,指针移动到下一个元素 [1|2,3]
peekingIterator.peek(); // 返回 2,指针不移动 [1|2,3]
peekingIterator.next(); // 返回 2,指针移动到下一个元素 [1,2|3]
peekingIterator.next(); // 返回 3,指针移动到下一个元素 [1,2,3|]
peekingIterator.hasNext(); // 返回 False
提示
1 <= nums.length <= 10001 <= nums[i] <= 1000- 对
peek和next的调用均有效 next、hasNext和peek最多调用1000次
解题思路
本题可以使用两种方法来实现:
-
缓存法:
- 使用一个变量缓存下一个元素
- peek()时返回缓存的元素
- next()时返回缓存并更新缓存
-
装饰器模式:
- 封装原始迭代器
- 添加缓存功能
- 维护迭代器状态
图解思路
迭代器状态分析表
| 操作 | 缓存值 | 迭代器状态 | 返回值 | 说明 |
|---|---|---|---|---|
| 初始化 | 1 | [1,2,3] | - | 缓存第一个元素 |
| next() | 2 | [2,3] | 1 | 返回缓存并更新 |
| peek() | 2 | [2,3] | 2 | 返回缓存不更新 |
| next() | 3 | [3] | 2 | 返回缓存并更新 |
| next() | null | [] | 3 | 返回缓存并清空 |
| hasNext() | null | [] | false | 无更多元素 |
方法调用流程表
| 方法 | 前置条件 | 后置条件 | 副作用 |
|---|---|---|---|
| peek() | 有缓存 | 不变 | 无 |
| next() | 有缓存 | 更新缓存 | 移动指针 |
| hasNext() | - | 不变 | 无 |
代码实现
C# 实现
public class PeekingIterator {
private Iterator<int> iterator;
private int? nextElement;
public PeekingIterator(Iterator<int> iterator) {
this.iterator = iterator;
// 初始化时缓存第一个元素
if (iterator.HasNext()) {
nextElement = iterator.Next();
}
}
public int Peek() {
return nextElement ?? throw new InvalidOperationException();
}
public int Next() {
int result = nextElement ?? throw new InvalidOperationException();
// 更新缓存
nextElement = iterator.HasNext() ? iterator.Next() : null;
return result;
}
public bool HasNext() {
return nextElement != null;
}
}
Python 实现
class PeekingIterator:
def __init__(self, iterator):
"""
Initialize your data structure here.
:type iterator: Iterator
"""
self.iterator = iterator
self.next_element = iterator.next() if iterator.hasNext() else None
def peek(self):
"""
Returns the next element in the iteration without advancing the iterator.
:rtype: int
"""
if self.next_element is None:
raise StopIteration()
return self.next_element
def next(self):
"""
:rtype: int
"""
if self.next_element is None:
raise StopIteration()
result = self.next_element
self.next_element = self.iterator.next() if self.iterator.hasNext() else None
return result
def hasNext(self):
"""
:rtype: bool
"""
return self.next_element is not None
C++ 实现
class PeekingIterator : public Iterator {
private:
int nextElement;
bool hasNextElement;
public:
PeekingIterator(const vector<int>& nums) : Iterator(nums) {
// 初始化时缓存第一个元素
hasNextElement = Iterator::hasNext();
if (hasNextElement) {
nextElement = Iterator::next();
}
}
int peek() {
if (!hasNextElement) {
throw runtime_error("No element to peek");
}
return nextElement;
}
int next() {
if (!hasNextElement) {
throw runtime_error("No element to return");
}
int result = nextElement;
hasNextElement = Iterator::hasNext();
if (hasNextElement) {
nextElement = Iterator::next();
}
return result;
}
bool hasNext() const {
return hasNextElement;
}
};
执行结果
C# 实现
- 执行用时:76 ms
- 内存消耗:25.4 MB
Python 实现
- 执行用时:32 ms
- 内存消耗:14.9 MB
C++ 实现
- 执行用时:0 ms
- 内存消耗:7.4 MB
性能对比
| 语言 | 执行用时 | 内存消耗 | 特点 |
|---|---|---|---|
| C# | 76 ms | 25.4 MB | 代码结构清晰,性能适中 |
| Python | 32 ms | 14.9 MB | 代码最简洁,性能不错 |
| C++ | 0 ms | 7.4 MB | 性能最优,内存占用最小 |
代码亮点
- 🎯 使用缓存机制实现peek功能
- 💡 优雅处理边界情况
- 🔍 异常处理完善
- 🎨 代码结构清晰,易于扩展
常见错误分析
- 🚫 未正确处理空迭代器
- 🚫 peek后未保持状态
- 🚫 next操作未更新缓存
- 🚫 hasNext判断错误
解法对比
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 缓存法 | O(1) | O(1) | 实现简单 | 需要额外空间 |
| 装饰器模式 | O(1) | O(1) | 结构清晰 | 代码较多 |
| 双指针法 | O(1) | O(1) | 无需缓存 | 不适用于流式数据 |
相关题目
- LeetCode 281. 锯齿迭代器 - 中等
- LeetCode 251. 展开二维向量 - 中等
- LeetCode 341. 扁平化嵌套列表迭代器 - 中等
📖 系列导航
🔥 算法专题合集 - 查看完整合集
📢 关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新第284题。
💬 互动交流
感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道算法题。
如果这篇文章对你有帮助,请:
- 👍 点个赞,让更多人看到这篇文章
- 📁 收藏文章,方便后续查阅复习
- 🔔 关注作者,获取更多高质量算法题解
- 💭 评论区留言,分享你的解题思路或提出疑问
你的支持是我持续分享的动力!
💡 一起进步:算法学习路上不孤单,欢迎一起交流学习!