LeetCode341 Flatten Nested List Iterator(迭代器模式实践) Java

题目:
Given a nested list of integers, implement an iterator to flatten it.

Each element is either an integer, or a list – whose elements may also be integers or other lists.

Example 1:
Given the list [[1,1],2,[1,1]],

By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,1,2,1,1].

Example 2:
Given the list [1,[4,[6]]],

By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,4,6].
分析:
先来看看leetcode给出的接口定义:

public class NestedIterator implements Iterator<Integer> {

    public NestedIterator(List<NestedInteger> nestedList) {

    }

    @Override
    public Integer next() {

    }

    @Override
    public boolean hasNext() {

    }
}
// This is the interface that allows for creating nested lists.
// You should not implement it, or speculate about its implementation
public interface NestedInteger {

    // @return true if this NestedInteger holds a single integer, rather than a
    // nested list.
    public boolean isInteger();

    // @return the single integer that this NestedInteger holds, if it holds a
    // single integer
    // Return null if this NestedInteger holds a nested list
    public Integer getInteger();

    // @return the nested list that this NestedInteger holds, if it holds a
    // nested list
    // Return null if this NestedInteger holds a single integer
    public List<NestedInteger> getList();
}

综合题目和给出的接口定义,我们知道每一个NestedInteger实际上是一棵树,所有的Integer都在树的叶子节点上(树的叶子节点不全是Integer,还包括空List),我们要遍历的nestedList其实就是NestedInteger组成的森林。对nestedList中树的遍历,我们从左向右遍历,对每一颗树,我们采用后根遍历,最后得出的结果就是题目要求的。我是根据这个思路来完成这道题的。
即使我们清楚了NestedInteger的组织形式是一棵树,也清楚后根遍历树的算法,要遍历NestedInteger也不是件容易的事儿。因为题目要求实现迭代器,意味着我们要保存遍历的下一个节点的位置,所以我讲基于迭代的方式遍历题中给定集合比遍历二叉树难在哪里(基于迭代的方式后根遍历二叉树要用栈记录遍历的下一个元素,具体内容见LeetCode145 Binary Tree Postorder Traversal(基于迭代))。其中最大的难点就是NestedInteger的叶子节点可能不是Integer,也可能是空List。对于二叉树,当我们寻找下一个(叶子)元素的时候,我们只需要一直往下走,而这里,当我们往下走遇到空List时,我们还需要往上走,再往下走,如果感觉空洞,可以直接看代码。第二个难点在于,二叉树中最多只有两个孩子节点,我们用一个栈就能记录下一个元素的位置,而这里的树可能有很多节点,我们不仅需要记录从根到该节点的路径上的所有节点,还需要记录每一个节点在父节点的索引位置。在我的实现中就是用两个栈和一个整数来记录下一个元素的位置的。
根据上一段的描述,我们知道,直接遍历题目中给的集合,太不容易了。可是,当我们实现了题目要求的该集合的迭代器,再来遍历这个集合,就太太简单了。这就是迭代器的威力。关于迭代器,有个迭代器模式,这个模式的思想就是封装遍历一个集合的行为。本题是我见过的最能诠释迭代器模式思想的例子了,所以分享给大家。
下面是我对本题的解答:

public class NestedIterator implements Iterator<Integer> {
    // 这其实是一个森林,森林里有nestedList.size()这么多棵树
    private List<NestedInteger> nestedList;
    // 从上到下的list,每个list的元素个数都大于0
    LinkedList<List<NestedInteger>> ancestorsStack = new LinkedList<>();
    // 与ancestorsStack对应,记录每个List<NestedInteger>在父list中的索引位置,
    // 根list在父list中的位置设为-1
    LinkedList<Integer> positionsStack = new LinkedList<Integer>();
    // 下一个元素(Integer)在父list中的索引位置
    private int integerIndex = 0;

    public NestedIterator(List<NestedInteger> nestedList) {
        this.nestedList = nestedList;

        if (nestedList.size() == 0) {
            return;
        }
        ancestorsStack.push(nestedList);
        positionsStack.push(-1);
        // 第一个被检查的元素
        NestedInteger cursor = nestedList.get(0);
        // 第一个被检查的元素在父list中的索引位置
        int thisIndex = 0;
        findNextElement(cursor, thisIndex);
    }

    @Override
    public Integer next() {
        Integer result = ancestorsStack.peek().get(integerIndex).getInteger();

        if (integerIndex != ancestorsStack.peek().size() - 1
                && ancestorsStack.peek().get(integerIndex + 1).isInteger()) {
            // 直接指向右边的Integer
            integerIndex++;
            return result;
        }

        int indexTemp = integerIndex;
        // 往上
        if (integerIndex == ancestorsStack.peek().size() - 1) {
            // 寻找ancestorsStack中下一个元素的最近祖先
            ancestorsStack.pop();
            indexTemp = positionsStack.pop();

            while (!ancestorsStack.isEmpty()) {
                List<NestedInteger> temp = ancestorsStack.peek();
                int size = temp.size();
                if (indexTemp != size - 1) {
                    break;
                } else {
                    ancestorsStack.pop();
                    indexTemp = positionsStack.pop();
                }

            }
        }

        // 这里隐含了integerIndex != ancestorsStack.peek().size() - 1
        // && !ancestorsStack.peek().get(integerIndex + 1).isInteger()的情况,因为
        // 这种情况下,ancestorsStack、positionsStack、start和indexTemp已经处于我们希望的
        // 状态

        if (ancestorsStack.isEmpty()) {
            return result;
        }

        // 以ancestorsStack.peek().get(indexTemp + 1)作为起点寻找第一个Integer
        NestedInteger start = ancestorsStack.peek().get(indexTemp + 1);
        int thisIndex = indexTemp + 1;
        findNextElement(start, thisIndex);

        return result;
    }

    /**
     * 以ancestorsStack、positions、start和startIndex共同定位的元素为起点,搜索下一个Integer
     * @param cursor 第一个被检查的元素
     * @param thisIndex 第一个被检查的元素在父list中的索引位置
     */
    private void findNextElement(NestedInteger start, int startIndex) {
        NestedInteger cursor = start;
        int thisIndex = startIndex;
        boolean findNext = false;
        while (!findNext) {
            if (cursor.isInteger()) {
                integerIndex = thisIndex;
                findNext = true;
            } else {
                if (cursor.getList().size() != 0) {
                    // 往下走
                    ancestorsStack.push(cursor.getList());
                    positionsStack.push(thisIndex);
                    thisIndex = 0;
                    cursor = cursor.getList().get(thisIndex);
                } else {
                    if (thisIndex < ancestorsStack.peek().size() - 1) {
                        // 往右走
                        thisIndex++;
                        cursor = ancestorsStack.peek().get(thisIndex);
                    } else {
                        // 往上走
                        ancestorsStack.pop();
                        thisIndex = positionsStack.pop();
                        while (!ancestorsStack.isEmpty()) {
                            List<NestedInteger> temp = ancestorsStack.peek();
                            int size = temp.size();
                            if (thisIndex != size - 1) {
                                thisIndex++;
                                cursor = ancestorsStack.peek().get(thisIndex);
                                break;
                            } else {
                                ancestorsStack.pop();
                                thisIndex = positionsStack.pop();
                            }

                        }
                        if (ancestorsStack.isEmpty()) {
                            break;
                        }
                    }
                }
            }
        }
    }

    @Override
    public boolean hasNext() {
        return !ancestorsStack.isEmpty();
    }

    @Override
    public void remove() {
        // TODO Auto-generated method stub

    }

}

本文提供了解该题最直观的方法,但不是最优的,大家可以在此基础上任意优化。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值