Converting Recursive Traversal to Iterator
In this article, I'm going to introduce a general pattern named Lazy Iterator for converting recursive traversal to iterator. Let's start with a familiar example of inorder traversal of binary tree.
It is straightforward to write a recursive function which returns the nodes as List:
// Java
List<Node> traverseInOrder(Node node) {
if (node == null) {
return emptyList();
}
Return concat(traverseInOrder(node.left), List.of(node), traverseInOrder(node.right));
}
List<T> concat(List<T>... lists) {
... // List concatenation.
}
Of course, we can simply return the iterator of the result list, but the drawback is twofold: 1) it is not lazily evaluated, which means even if we only care about the first a few items, we must traverse the whole tree. 2) the space complexity is O(N), which is not really necessary. We'd like to have an iterator which is lazily evaluated and takes memory only as needed. The function signature is given as below:
// Java
Iterator<Node> traverseInOrder(Node node) {
// TODO
}
One idea most people could easily come up with (but not necessarily be able to correctly implement) is to use a stack to keep track of the state of traversal. That works, but it is relatively complex and not general, there is a neat, simple and general way. This is the code which demonstrates the pattern:
// Java
Iterator<Node> traverseInOrder(Node node) {
if (node == null) {
return emtpyIterator();
}
Supplier<Iterator<Node>> leftIterator = () -> traverseInOrder(node.left);
Supplier<Iterator<Node>> currentIterator = () -> singleNodeIterator(node);
Supplier<Iterator<Node>> rightIterator = () -> traverseInOrder(node.right);
return concat(leftIterator, currentIterator, rightIterator);
}
Iterator<T> concat(Supplier<Iterator<T>>... iteratorSupplier) {
// TODO
}
If you are not familiar with Java, Supplier<T>
is a function which takes no argument and returns a value of type T, so basically you can think of it as a lazily evaluated T.
Note the structural correspondence between the code above and the original recursive traversal function. Instead of traverse the left and right branches before returning, it creates a lambda expression for each which when evaluated will return an iterator. So the iterators for the subproblems are lazily created.
Finally and the most importantly, it needs the help of a general function concat
which creates an iterator backed by multiple iterator suppliers. The type of this function is (2 suppliers):
Supplier<Iterator<T>> -> Supplier<Iterator<T>> -> Iterator<T>
The key is that this function must keep things lazy as well, evaluate only when necessary. Here is how I implement it:
// Java
Iterator<T> concat(Supplier<Iterator<T>>... iteratorSuppliers) {
return new LazyIterator(iteratorSuppliers);
}
class LazyIterator<T> {
private final Supplier<Iterator<T>>[] iteratorSuppliers;
private int i = 0;
private Iterator<T> currentIterator = null;
public LazyIterator(Supplier<Iterator<T>>[] iteratorSuppliers) {
this.iteratorSuppliers = iteratorSuppliers;
}
// When returning true, hasNext() always sets currentIterator to the correct iterator
// which has more elements.
@Override
public boolean hasNext() {
while (i < iteratorSuppliers.length) {
if (currentIterator == null) {
currentIterator = iteratorSuppliers[i].apply();
}
if (currentIterator.hasNext()) {
return true;
}
currentIterator = null;
i++;
}
return false;
}
@Override
public T next() {
return currentIterator.next();
}
}
The LazyIterator
class works only on Iterator<T>, which means it is orthogonal to specific recursive functions, hence it serves as a general way of converting recursive traversal into iterator.
If you are familiar with languages with generator, e.g., Python, the idiomatic way of implementing iterator for recursive traversal is like this:
# Python
def traverse_inorder(node):
if node.left:
for child in traverse_inorder(node.left):
yield child
yield node
if node.right:
for child in traverse_inorder(node.right):
yield child
yield
will save the current stack state for the generator function, and resume the computation later when being called again. You can think of the lazy iterator pattern introduced in this article as a way of capturing the computation which can be resumed, hence simulate the generator feature in other languages.
Converting Recursive Traversal to Iterator的更多相关文章
- Java性能提示(全)
http://www.onjava.com/pub/a/onjava/2001/05/30/optimization.htmlComparing the performance of LinkedLi ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
- java.util.Map源码分析
/** * An object that maps keys to values. A map cannot contain duplicate keys; * each key can map to ...
- [leetcode]428. Serialize and Deserialize N-ary Tree序列化与反序列化N叉树
Serialization is the process of converting a data structure or object into a sequence of bits so tha ...
- jdk1.8新特性之接口default方法
众所周知,default是java的关键字之一,使用场景是配合switch关键字用于条件分支的默认项.但自从java的jdk1.8横空出世以后,它就被赋予了另一项很酷的能力——在接口中定义非抽象方法. ...
- java面试基础篇(一)
最近想深入的理解一下java 的工作机制,也是便于后期的面试. 1.A:HashMap和Hashtable有什么区别? Q:HashMap和Hashtable都实现了Map接口,因此很多特性非常相似. ...
- Java-Class-I:java.util.Map
ylbtech-Java-Class-I:java.util.Map 1.返回顶部 1.1. import java.util.HashMap; import java.util.Map; 1.2. ...
- Java的集合(一)
转载:https://blog.csdn.net/hacker_zhidian/article/details/80590428 Java集合概况就三个:List.set和map list(Array ...
- Tree 使用方式
Traditional Ways of Tree Traversal This page contains examples of some “standard” traversal algorith ...
随机推荐
- pm2管理node
一般直接npm start起的退出命令行就没了,node后台管理工具pm2目前比较流行. npm install -g pm2 pm2 list pm2 start bin/www --name de ...
- 《剑指Offer》第20题(Java实现):定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
一.题目描述 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1)). 二.思路解析 首先定义一个Integer类型的栈,记为stack,此栈用来完成数据 ...
- 获取Control请求路径
对于多个uri映射到同一个control方法时,需根据不同的uri返回的数据结构进行区分,因此需要再方法体内获取到RequestUri,再对其做相应的判断实现对应的业务逻辑 @Resource pri ...
- Pytorch之训练器设置
Pytorch之训练器设置 引言 深度学习训练的时候有很多技巧, 但是实际用起来效果如何, 还是得亲自尝试. 这里记录了一些个人尝试不同技巧的代码. tensorboardX 说起tensorflow ...
- Mysql必知必会 第一章 了解SQL
第一章 了解SQL 1.1 数据库基础 1.1.1 什么是数据库 数据库的定义:保存有组织的数据的容器 数据库软件不是数据库,而是DBMS 1.1.2 表 表(Table)的定义:某种特定类型数据的结 ...
- 面试简单整理之web
63.servlet是什么?运行过程? Servlet是一门用于开发动态web资源的技术. 运行过程: Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后: ①W ...
- 团队Scrum冲刺阶段-Day 6
选择困难症的福音--团队Scrum冲刺阶段-Day 6 今日进展 编写提问部分 游戏分类的界面全部写完了!!!! 临时大家决定没有BGM的app不是一个完整的app,所以在大家共同学习的努力下,听完四 ...
- redis---安装和开启和关闭
转redis---安装和开启和关闭 http://blog.csdn.net/xing_____/article/details/38457463 系统:centos6.4 redis下载:http: ...
- 使用Tenorshare iCareFone for mac为iPhone做系统修复
tenorshare icarefonemac中文版采用一键式方法来保护,修理,清洁,优化并最终加快您的iPhone,iPad和iPod的速度.它可以帮助您轻松解决所有iOS问题,并让您的iPhone ...
- java 多线程中的wait方法的详解
java多线程中的实现方式存在两种: 方式一:使用继承方式 例如: PersonTest extends Thread{ String name; public PersonTest(String n ...