锯齿迭代器

Submission

运行时间: 33 ms

内存: 16.3 MB

from collections import deque
class ZigzagIterator:
    def __init__(self, v1: List[int], v2: List[int]):
        self.queue = deque()
        for arr in [v1, v2]:
            if arr: self.queue.append((arr, 0))
        
    def next(self) -> int:
        val = None
        arr, idx = self.queue.popleft()
        val = arr[idx]
        idx += 1
        if idx < len(arr):
            self.queue.append((arr, idx))
        

        return val
        

    def hasNext(self) -> bool:
        for arr, idx in self.queue:
            if idx < len(arr):
                return True
        return False

# Your ZigzagIterator object will be instantiated and called as such:
# i, v = ZigzagIterator(v1, v2), []
# while i.hasNext(): v.append(i.next())

Explain

这个题解使用队列(deque)来实现锯齿迭代器。初始化时,将非空的输入数组及其初始下标 0 作为元组依次加入队列。每次调用 next() 方法时,从队首取出当前要迭代的数组和下标,返回当前元素,并将下标加 1。如果更新后的下标没有超出数组范围,则将新的元组加入队尾。hasNext() 方法则遍历队列中的所有元组,判断是否存在还未迭代完的数组。

时间复杂度: O(m+n+k)

空间复杂度: O(1)

from collections import deque
class ZigzagIterator:
    def __init__(self, v1: List[int], v2: List[int]):
        # 初始化队列
        self.queue = deque()
        # 将非空数组及其初始下标加入队列
        for arr in [v1, v2]:
            if arr: self.queue.append((arr, 0))
        
    def next(self) -> int:
        val = None
        # 从队首取出当前数组和下标
        arr, idx = self.queue.popleft()
        # 获取当前元素
        val = arr[idx]
        # 将下标加 1
        idx += 1
        # 如果更新后的下标没有超出数组范围,将新元组加入队尾
        if idx < len(arr):
            self.queue.append((arr, idx))
        
        return val
        

    def hasNext(self) -> bool:
        # 遍历队列中的所有元组,判断是否存在还未迭代完的数组
        for arr, idx in self.queue:
            if idx < len(arr):
                return True
        return False

# Your ZigzagIterator object will be instantiated and called as such:
# i, v = ZigzagIterator(v1, v2), []
# while i.hasNext(): v.append(i.next())

Explore

在锯齿迭代器的设计中,使用`deque`(双端队列)是因为它支持从两端高效地添加和删除元素。当使用列表时,从列表的开头删除元素的时间复杂度为O(n),因为需要移动其他所有元素。而在链表中,虽然添加和删除操作的时间复杂度为O(1),但链表在随机访问时不如`deque`高效。`deque`提供了平衡的方案,即支持快速的随机访问以及从两端高效的插入和删除,这适合锯齿迭代器中经常进行的操作。

是的,如果输入的数组长度不同,按照当前的实现,较短的数组可能会比较长的数组先迭代完成。为了处理这种情况并保持锯齿形的迭代,可以在迭代过程中动态调整各个数组元素的取出顺序。具体地,可以在元素取出后立即将其(如果仍有剩余元素)放回队列,这样可以确保在所有数组中都尽可能均匀地进行迭代。

在`next`方法中,当当前数组已迭代完成,实际上我们是直接丢弃队列中的这个元组,而不是再次加入队列。这种设计的优点是保持了队列的简洁性,只有未完全迭代的数组才保留在队列中,这有助于减少无效的检查和操作。缺点可能是需要在每次调用`next`时进行条件检查,但这是管理动态数据集的必要操作,以确保不会访问已经迭代完毕的数组。

在`hasNext`方法中遍历队列确实可能影响性能,特别是当队列较大时。为了优化这个方法,可以考虑使用一个标志来跟踪是否还有更多的元素可以迭代。每次调用`next`时,如果发现数组已迭代完毕,则更新这个标志。这样,`hasNext`就可以直接返回这个标志的状态,从而避免每次都遍历队列。这个方法可以显著减少在元素接近尾部时的计算量。