扁平化嵌套序列


4.10 扁平化嵌套序列

4.10.1 问题描述

给你一个嵌套的整数列表 nestedList 。每个元素要么是一个整数,要么是一个列表;该列表的元素也可能是整数或者是其他列表。请你实现一个迭代器将其扁平化,使之能够遍历这个列表中的所有整数。

实现扁平迭代器类 NestedIterator :

NestedIterator(List nestedList) 用嵌套列表 nestedList 初始化迭代器。
int next() 返回嵌套列表的下一个整数。
boolean hasNext() 如果仍然存在待迭代的整数,返回 true ;否则,返回 false 。
你的代码将会用下述伪代码检测:

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/flatten-nested-list-iterator
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 有一种数据结构叫做NestedInteger,这个结构中存的数据可能是一个Integer整数,也有可能是是一个NestedInteger的列表,注意,这个列表里面装的是NestdInteger,也就是说这个列表中的每一个元素可能是个整数,也可能是个列表,这样无限递归嵌套下去

  • 我们到的算法将被输入一个NestedInteger列表,需要做的就是写一个迭代器类,将这个带有嵌套结构的NestedInteger给拍平

public class NestedIterator implements Iterator<Integer> {

    public NestedIterator(List<NestedInteger> nestedList) {
        
    }

    @Override
    public Integer next() {
        
    }

    @Override
    public boolean hasNext() {
        
    }
}
  • 我们写的这个类将被这样调用,先调用hasNext()方法,然后调用next方法
while iterator.hasNext()
    append iterator.next() to the end of res
return res
  • 样例分析:假设输入[[1,1],2,[1,1]],其中存在三个NestedInteger,两个列表类型的NestedInteger和一个整数类型的NestedInteger,算法返回打平的列表[1,1,2,1,1]
  • 假设输入[1,[4,[6]]],算法返回打平的列表[1,4,6]
  • 迭代器模式是设计模式的一种,它为调用者屏蔽底层数据结构的细节,简单通过next()和hasNext()方法有序地进行遍历
  • 回想这个算法问题,NestedInteger结构实际上是一种支持无限嵌套的数据结构,而且可以同时表示整数和列表两种不同的类型

4.10.2 解题思路

public class NestedInteger {
    private Integer val;
    private List<NestedInteger> list;

    public NestedInteger(Integer val){
        this.val = val;
        this.list = null;
    }
    public NestedInteger(List<NestedInteger> list){
        this.list = list;
    }
    //如果其中存储的是一个整数,则返回true,否则返回false
    public boolean isInteger(){
        return this.val!=null;
    }
    //如果存储的是整数,则返回这个整数,否则返回null
    public Integer getVal(){
        return this.val;
    }
    //如果存储的是列表,则返回这个列表
    public List<NestedInteger> getList() {
        return list;
    }
}
  • 由题意猜测其内部数据结构应该是类似这样的结构
  • 对比N叉树的定义:其叶子节点是Integer类型,其val字段非空,其他节点都是List<leetocde_1.TreeNode>类型,其val字段为空,但是list字段非空,存着孩子节点
class leetocde_1.TreeNode{
    int val;
    List<leetocde_1.TreeNode> children
}
  • 那么既然是N叉树的序列化问题,那么就可以转化为N叉树的遍历问题,将所有的叶子节点都给拿出来,就可以作为迭代器进行遍历了
void traverse(leetocde_1.TreeNode root){
    //根节点该怎么做:把自己的所有子节点都给拿出来,遍历,剩下的抛给递归
    for(leetocde_1.TreeNode node:root.children){
        traverse(node);
    }
}
  • 这个框架可以遍历所有的节点,而我们只对整数类型的NestedInteger感兴趣,也就是只想要叶子节点,所以traverse函数只要在到达叶子节点的时候把val加入结果列表即可
private Iterator<Integer> it;

public NestedIterator(List<NestedInteger> nestedList) {
    //存放将nestedList打平的结果
    List<Integer> result = new LinkedList<>();
    for(NestedInteger node:nestedList){
        this.traverse(node,result);
    }
    //得到result列表的迭代器
    this.it = result.iterator();
}

private void traverse(NestedInteger root,List<Integer> result){
    if(root.isInteger()){
        result.add(root.getInteger());
        return;
    }
    for(NestedInteger node:root.getList()){
        this.traverse(node,result);
    }
}

@Override
public Integer next() {
    return it.next();
}

@Override
public boolean hasNext() {
    return it.hasNext();
}

4.10.3 进阶思路

  • 一般的迭代器求值应该是惰性的,也就是说,如果你只要一个结果,那么我就算一个(或者是一小部分)结果出来,而不是一次把所有结果都算出来,而在上述解法中,一次性算出了所有叶子节点的值,全部装到了result列表,也就是内存中,next和hasNext只是在对result做迭代,如果输入的规模非常大,构造函数中的计算就会很慢,而且很占用内存
  • 解决思路:调用hasNext()时,如果nestedList的第一个元素是列表类型,则不断展开这个元素,直到第一个元素是整数类型,由于调用next()方法之前肯定会调用hasNext,这样就可以保证每次调用next方法的时候列表的第一个元素是整数类型,直接返回并删除第一个元素即可
private LinkedList<NestedInteger> list;

public NestedIterator(List<NestedInteger> nestedList) {
    //不直接用nestedList的引用,是因为不能确定它的底层实现
    //必须保证是LinkedList,否则addFirst会很低效
    list = new LinkedList<>(nestedList);
}

@Override
public Integer next() {
    //hasNext()方法保证了第一个元素一定是整数类型
    return list.remove(0).getInteger();
}

@Override
public boolean hasNext() {
    //循环拆分列表,直到第一个元素是整数类型
    while(!list.isEmpty() && !list.get(0).isInteger()){
        //当列表第一个元素是列表类型时,进入循环
        List<NestedInteger> first = list.remove(0).getList();
        //将第一个列表打平并按照顺序添加到开头
        for(int i = first.size()-1;i>=0;i--){
            list.addFirst(first.get(i));
        }
    }
    return !list.isEmpty();
}

文章作者: 穿山甲
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 穿山甲 !
  目录