【LeetCode.284】顶端迭代器

题目:顶端迭代器

请你设计一个迭代器,除了支持 hasNextnext 操作外,还支持 peek 操作。

实现 PeekingIterator 类:

PeekingIterator(int[] nums) 使用指定整数数组 nums 初始化迭代器。
int next() 返回数组中的下一个元素,并将指针移动到下个元素处。
bool hasNext() 如果数组中存在下一个元素,返回 true ;否则,返回 false
int peek() 返回数组中的下一个元素,但 移动指针。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
输入:
["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 <= 1000
1 <= nums[i] <= 1000
对 next 和 peek 的调用均有效
next、hasNext 和 peek 最多调用 1000 次

进阶:你将如何拓展你的设计?使之变得通用化,从而适应所有的类型,而不只是整数型?


分析:

顶端迭代器需要实现以下三种操作:

next:返回迭代器的下一个元素,并将指针向后移动一位;

hasNext:判断迭代器中是否还有剩余的元素;

peek:返回迭代器的下一个元素,不改变指针。

每种编程语言自带的迭代器可能支持上述一种或多种操作,但是不一定支持上述全部操作。如果编程语言自带的迭代器本身就支持上述操作,可以直接使用,否则需要自定义实现。

  1. C++中PeekingIterator继承父类IteratorIterator已经实现方法nexthasNext,在此我们在PeekingIterator中主要实现peek方法即可。我们使用flag标记迭代器是否还有剩余元素,使用nextElement存储迭代器的下一个元素。

next:首先用ret存储nextElement表示返回值,flag保存Iterator调用hasNext方法的返回结果,然后将nextElement向后移动一位,最后返回ret

hasNext:返回flag

peek:由于peek操作不改变指针,因此返回nextElement

  1. C#的IEnumerator接口包含属性Current和方法MoveNext(该方法的返回值类型是bool,表示是否成功移动到下一个元素),三种操作都需要自定义实现,需要使用flag存储迭代器是否还有剩余的元素。

next:首先用ret存储iterator.Current表示返回值,然后对iterator调用MoveNext方法使其向后移动一位并将该方法的结果赋值给flag,最后返回ret

hasNext:返回flag;

peek:由于peek操作不改变指针,因此返回iterator.Current


题解:

  1. C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class PeekingIterator : public Iterator {
public:
PeekingIterator(const vector<int>& nums) : Iterator(nums) {
// Initialize any member here.
// **DO NOT** save a copy of nums and manipulate it directly.
// You should only use the Iterator interface methods.
flag = Iterator::hasNext();
if(flag){
nextElement = Iterator::next();
}
}

// Returns the next element in the iteration without advancing the iterator.
int peek() {
return nextElement;
}

// hasNext() and next() should behave the same as in the Iterator interface.
// Override them if needed.
int next() {
int ret = nextElement;
flag = Iterator::hasNext();
if(flag){
nextElement = Iterator::next();
}
return ret;
}

bool hasNext() const {
return flag;
}
private:
bool flag;
int nextElement;
};
  1. C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class PeekingIterator {
private IEnumerator<int> iterator;
private bool flag;

// iterators refers to the first element of the array.
public PeekingIterator(IEnumerator<int> iterator) {
// initialize any member here.
this.iterator = iterator;
flag = true;
}

// Returns the next element in the iteration without advancing the iterator.
public int Peek() {
return iterator.Current;
}

// Returns the next element in the iteration and advances the iterator.
public int Next() {
int ret = iterator.Current;
flag = iterator.MoveNext();
return ret;
}

// Returns false if the iterator is refering to the end of the array of true otherwise.
public bool HasNext() {
return flag;
}
}

进阶:

进阶问题
进阶问题要求拓展顶端迭代器的设计,使其适用于所有类型,不局限于整数。

对于动态类型语言如 JavaScriptPython,不需要拓展上述设计。

对于静态类型语言如 JavaC#C++,可以通过使用泛型的方式拓展设计,在 PeekingIterator 类中定义泛型,使用时可以用任意类型。


总结:

以前习惯将迭代器当作指针来看到,因为感觉上是大同小异,现在加深了对两者区别的理解:迭代器像是有固定方法的指针,不像指针那么随意,更像是名称所展示的那样包含“迭代”的思想