• 4.10扁平化嵌套序列


    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() {
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 我们写的这个类将被这样调用,先调用hasNext()方法,然后调用next方法
    while iterator.hasNext()
        append iterator.next() to the end of res
    return res
    
    • 1
    • 2
    • 3
    • 样例分析:假设输入[[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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 由题意猜测其内部数据结构应该是类似这样的结构
    • 对比N叉树的定义:其叶子节点是Integer类型,其val字段非空,其他节点都是List类型,其val字段为空,但是list字段非空,存着孩子节点
    class TreeNode{
        int val;
        List<TreeNode> children
    }
    
    • 1
    • 2
    • 3
    • 4
    • 那么既然是N叉树的序列化问题,那么就可以转化为N叉树的遍历问题,将所有的叶子节点都给拿出来,就可以作为迭代器进行遍历了
    void traverse(TreeNode root){
        //根节点该怎么做:把自己的所有子节点都给拿出来,遍历,剩下的抛给递归
        for(TreeNode node:root.children){
            traverse(node);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 这个框架可以遍历所有的节点,而我们只对整数类型的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();
        }
    
    • 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

    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();
        }
    
    • 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
  • 相关阅读:
    【pandas数据分析】pandas数据结构
    Linux 简介
    维修上门预约系统简单讲
    执行计划--mysql详解(七)
    ChatGPT - 高效编写Prompt
    工具篇--分布式定时任务springBoot--elasticjob简单使用(1)
    java计算机毕业设计工资管理系统MyBatis+系统+LW文档+源码+调试部署
    借助拧紧曲线高效管理螺栓装配防错——SunTorque智能扭矩系统
    python实现分词器
    NPS:使用 Windows NPS Server 部署 802.1X 无线认证(2)
  • 原文地址:https://blog.csdn.net/weixin_50340097/article/details/126568339