341. 扁平化嵌套列表迭代器

 

341. 扁平化嵌套列表迭代器

难度中等269收藏分享切换为英文接收动态反馈

给你一个嵌套的整型列表。请你设计一个迭代器,使其能够遍历这个整型列表中的所有整数。

列表中的每一项或者为一个整数,或者是另一个列表。其中列表的元素也可能是整数或是其他列表。

 

示例 1:

输入: [[1,1],2,[1,1]]
输出: [1,1,2,1,1]
解释: 通过重复调用 next 直到 hasNext 返回 false,next 返回的元素的顺序应该是: [1,1,2,1,1]

示例 2:

输入: [1,[4,[6]]]
输出: [1,4,6]
解释: 通过重复调用 next 直到 hasNext 返回 false,next 返回的元素的顺序应该是: [1,4,6]

# """

# This is the interface that allows for creating nested lists.

# You should not implement it, or speculate about its implementation

# """

#class NestedInteger:

#    def isInteger(self) -> bool:

#        """

#        @return True if this NestedInteger holds a single integer, rather than a nested list.

#        """

#

#    def getInteger(self) -> int:

#        """

#        @return the single integer that this NestedInteger holds, if it holds a single integer

#        Return None if this NestedInteger holds a nested list

#        """

#

#    def getList(self) -> [NestedInteger]:

#        """

#        @return the nested list that this NestedInteger holds, if it holds a nested list

#        Return None if this NestedInteger holds a single integer

#        """

 

class NestedIterator:

    # def dfs(self,nests):

    #     for nest in nests:

    #         if nest.isInteger():

    #             self.queue.append(nest)

    #         else:

    #             self.dfs(nest.getList())

    # def __init__(self, nestedList: [NestedInteger]):

    #     self.queue = collections.deque()

    #     self.dfs(nestedList)

 

    # def next(self) -> int:

    #     return self.queue.popleft()

    

    # def hasNext(self) -> bool:

    #     return len(self.queue)

    def __init__(self, nestedList: [NestedInteger]):

        self.stack = []

        for i in range(len(nestedList)-1,-1,-1):

            self.stack.append(nestedList[i])

    def next(self) -> int:

        nest = self.stack.pop()

        return nest

    def hasNext(self) -> bool:

        while self.stack:

            cur =  self.stack[-1]

            if cur.isInteger():

                return True

            self.stack.pop()

            temp = cur.getList()

            for i in range(len(temp)-1,-1,-1):

                self.stack.append(temp[i])

        return False




 

# Your NestedIterator object will be instantiated and called as such:

# i, v = NestedIterator(nestedList), []

# while i.hasNext(): v.append(i.next())

 

 

 

 

 

【负雪明烛】详解题意,梳理递归和迭代两种思路

负雪明烛L6

发布于 11 小时前6.1kC++Python迭代递归算法

各位题友大家好! 今天是 @负雪明烛 坚持日更的第 58 天。今天力扣上的每日一题是「341. 扁平化嵌套列表迭代器」。

题意解析

今天的题意略难理解,需要我翻译一下,理解题意的朋友请跳过。

本题定义了一个类 NestedInteger ,这个类可以存储 int  或 List<NestedInteger> ;所以称它是一个「嵌套列表」。类似于一棵多叉树,每个节点都可以有很多子节点。

它有三个方法:

  • isInteger() ,判断当前存储的对象是否为 int;
  • getInteger() , 如果当前存储的元素是 int 型的,那么返回当前的结果 int,否则调用会失败;
  • getList() ,如果当前存储的元素是 List<NestedInteger> 型的,那么返回该 List,否则调用会失败。

而「扁平化嵌套列表迭代器」说的是,我们需要设计一个迭代器,这个迭代器是把「嵌套列表」铺平(拆包)成各个 int,然后每次调用 hasNext() 来判断是否有下一个整数,通过 next() 返回下一个整数

注意迭代器是一种按照特定顺序对数据结构遍历的方式,它的调用方式是:

i, v = NestedIterator(nestedList), []
while i.hasNext():
	v.append(i.next())

解题思路

本文有两种主要的思路:

  1. 在构造函数中提前「扁平化」整个嵌套列表;
  2. 在调用 hasNext() 或者 next() 方法的时候扁平化当前的嵌套的子列表

一、递归:构造函数中提前「扁平化」整个嵌套列表

这是最简单的方法,但是我认为这不是面试官想要的方法。

在构造函数中提前扁平化整个嵌套列表。那么在 hasNext() 或者 next() 可以很简单地返回迭代器位置的 int。

因为这个嵌套的数据结构有点类似于多叉树,所以我们可以按照类似地遍历思路:递归

承载遍历结果的数据结构可以使用数组,那么另外需要一个整数标记当前的迭代器指向的位置;也可以使用一个队列,每次调用 next() 方法的时候从队列的开头弹出一个元素。

递归的思路比较简单,我用的是队列:

  • 遍历输入的「嵌套列表」所有的元素,判断每个元素是 int 还是 list;
    • 如果当前元素是 int,放入队列尾部;
    • 如果当前元素是 list,那么需要对当前 子list 继续递归;

其余代码比较简单,不作赘述。

  • Python
class NestedIterator(object):
    def dfs(self, nests):
        for nest in nests:
            if nest.isInteger():
                self.queue.append(nest.getInteger())
            else:
                self.dfs(nest.getList())
                    
    def __init__(self, nestedList):
        self.queue = collections.deque()
        self.dfs(nestedList)

    def next(self):
        return self.queue.popleft()

    def hasNext(self):
        return len(self.queue)
  • 时间复杂度:构造函数的时间复杂度是 O(N)O(N); next() 和 hasNext() 的时间复杂度是 O(1)O(1)。
  • 空间复杂度:O(N)O(N)。

二、迭代:调用 hasNext() 或者 next() 方法的时候扁平化当前的嵌套的子列表

这个方法更加有挑战性,也是这个题最正确的解法。因为对于大部分情况,我们希望迭代器能够一边迭代一边获取当前的结果,而不是提前初始化好。

把递归方法 转 迭代方法,我们需要用到「栈」。

递归方法中,我们在遍历时如果遇到一个嵌套的 子list,就立即处理该 子list,直到全部展开;
迭代方法中,我们不需要全部展开,只需要把 当前list 的所有元素放入 list 中。

由于「栈」的先进后出的特性,我们需要逆序在栈里放入各个元素。

处理流程分为两步:

  1. 在构造函数中应该初始化,把当前列表的各个元素(不用摊平)逆序放入栈中。
  2. 在 hasNext() 方法中,访问(不弹出)栈顶元素,判断是否为 int:
  • 如果是 int 那么说明有下一个元素,返回 true;然后 next() 就会被调用,把栈顶的 int 弹出;
  • 如果是 list 需要把当前列表的各个元素(不用摊平)逆序放入栈中。
  • 如果栈为空,那么说明原始的嵌套列表已经访问结束了,返回 false。

算法整体的流程,通过举例说明。假如输入 [1, [2,3]] 。

1. 在构造函数中:栈里面放的应该是 stack = [[2, 3], 1]
2. 在调用 hasNext() 方法时,访问栈顶元素是 1,为 int,那么直接返回 true;
3. 然后调用 next() 方法,弹出栈顶元素 1;
4. 再调用 hasNext() 方法时,访问栈顶元素是 [2,3],为 list,那么需要摊平,继续放到栈中。
        当前的栈是 stack = [3, 2]
5. 然后调用 next() 方法,弹出栈顶元素 2;
6. 然后调用 next() 方法,弹出栈顶元素 3;
7. 再调用 hasNext() 方法时,栈为空,因此返回 false,迭代器运行结束。

这里需要说一下为什么在 hasNext() 方法中摊平 list,而不是在 next() 方法中。比如对于 [[]] 的输入, hasNext()  方法是判断其中是否有 int 元素了,则必须把内层的 list 摊平来看,发现是空的,返回 false。

代码如下:

  • Python
  • C++
class NestedIterator(object):

    def __init__(self, nestedList):
        self.stack = []
        for i in range(len(nestedList) - 1, -1, -1):
            self.stack.append(nestedList[i])
        

    def next(self):
        cur = self.stack.pop()
        return cur.getInteger()

    def hasNext(self):
        while self.stack:
            cur = self.stack[-1]
            if cur.isInteger():
                return True
            self.stack.pop()
            for i in range(len(cur.getList()) - 1, -1, -1):
                self.stack.append(cur.getList()[i])
        return False
  • 时间复杂度:构造函数的最坏时间复杂度是 O(N)O(N); next() 和 hasNext() 的最坏时间复杂度是 O(N)O(N)。
  • 空间复杂度:O(N)O(N)。

刷题心得

面试的时候需要写出方法二,今天的题目很好,请务必掌握呀。

参考资料:
力扣官方题解
341. Flatten Nested List Iterator


OK,以上就是 @负雪明烛 写的今天题解的全部内容了,如果你觉得有帮助的话,求赞、求关注、求收藏。如果有疑问的话,请在下面评论,我会及时解答。

关注我,你将不会错过我的精彩动画题解、面试题分享、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。

祝大家牛年大吉!AC 多多,Offer 多多!我们明天再见!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值