From a68a6522c2d580b989f9cb0c1a763dc5f0d83208 Mon Sep 17 00:00:00 2001 From: Abhinav Sarkar Date: Wed, 17 Jul 2019 10:35:59 +0530 Subject: [PATCH] Adds tree iterators based on CPS defunctionalization --- .../abhinavsarkar/algorist/TreeIterators.java | 541 ++++++++++++++++++ 1 file changed, 541 insertions(+) create mode 100644 src/main/java/net/abhinavsarkar/algorist/TreeIterators.java diff --git a/src/main/java/net/abhinavsarkar/algorist/TreeIterators.java b/src/main/java/net/abhinavsarkar/algorist/TreeIterators.java new file mode 100644 index 0000000..8c78b32 --- /dev/null +++ b/src/main/java/net/abhinavsarkar/algorist/TreeIterators.java @@ -0,0 +1,541 @@ +package net.abhinavsarkar.algorist; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Stack; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Consumer; +import java.util.function.Supplier; + +class TreeIerators { + public static void main(String[] args) { + Tree tree; + do { + tree = Tree.generate(4, () -> Utils.generateRandomAlphaString(2), 0.1); + } while (tree == null); + + System.out.println("Tree:"); + System.out.println(tree); + + // InOrderIterator + InOrderIterator.printRecursive(tree); + System.out.println("\tInOrderIterator.printRecursive"); + + InOrderIterator.iterateRecursive(tree, Utils::printContent); + System.out.println("\tInOrderIterator.iterateRecursive"); + + InOrderIterator.iterateCPS(tree, Utils::printContent, () -> {}); + System.out.println("\tInOrderIterator.iterateCPS"); + + InOrderIterator.iterateDefCPS(tree, Utils::printContent, null); + System.out.println("\tInOrderIterator.iterateDefCPS"); + + InOrderIterator.iterate(tree, Utils::printContent, null); + System.out.println("\tInOrderIterator.iterate"); + + InOrderIterator inOrderIterator = new InOrderIterator<>(tree); + while (inOrderIterator.hasNext()) { + Utils.printContent(inOrderIterator.next()); + } + System.out.println("\tInOrderIterator"); + System.out.println(); + + // PreOrderIterator + PreOrderIterator.printRecursive(tree); + System.out.println("\tPreOrderIterator.printRecursive"); + + PreOrderIterator.iterateRecursive(tree, Utils::printContent); + System.out.println("\tPreOrderIterator.iterateRecursive"); + + PreOrderIterator.iterateCPS(tree, Utils::printContent, () -> {}); + System.out.println("\tPreOrderIterator.iterateCPS"); + + PreOrderIterator.iterateDefCPS(tree, Utils::printContent, null); + System.out.println("\tPreOrderIterator.iterateDefCPS"); + + PreOrderIterator.iterate(tree, Utils::printContent, null); + System.out.println("\tPreOrderIterator.iterate"); + + PreOrderIterator preOrderIterator = new PreOrderIterator<>(tree); + while (preOrderIterator.hasNext()) { + Utils.printContent(preOrderIterator.next()); + } + System.out.println("\tPreOrderIterator"); + System.out.println(); + + // PostOrderIterator + PostOrderIterator.printRecursive(tree); + System.out.println("\tPostOrderIterator.printRecursive"); + + PostOrderIterator.iterateRecursive(tree, Utils::printContent); + System.out.println("\tPostOrderIterator.iterateRecursive"); + + PostOrderIterator.iterateCPS(tree, Utils::printContent, () -> {}); + System.out.println("\tPostOrderIterator.iterateCPS"); + + PostOrderIterator.iterateDefCPS(tree, Utils::printContent, null); + System.out.println("\tPostOrderIterator.iterateDefCPS"); + + PostOrderIterator.iterate(tree, Utils::printContent, null); + System.out.println("\tPostOrderIterator.iterate"); + + PostOrderIterator postOrderIterator = new PostOrderIterator<>(tree); + while (postOrderIterator.hasNext()) { + Utils.printContent(postOrderIterator.next()); + } + System.out.println("\tPostOrderIterator"); + } + + /* Run result: + Tree: + ├ r + │├ j + ││├ x + │││├ e + │││├ m + ││├ vz + │││├ g + ││├ + │├ l + ││├ b + │││├ qc + │││├ g + ││├ rp + │││├ d + │││├ o + + e x m j g vz r qc b g l d rp o InOrderIterator.printRecursive + e x m j g vz r qc b g l d rp o InOrderIterator.iterateRecursive + e x m j g vz r qc b g l d rp o InOrderIterator.iterateCPS + e x m j g vz r qc b g l d rp o InOrderIterator.iterateDefCPS + e x m j g vz r qc b g l d rp o InOrderIterator.iterate + e x m j g vz r qc b g l d rp o InOrderIterator + + r j x e m vz g l b qc g rp d o PreOrderIterator.printRecursive + r j x e m vz g l b qc g rp d o PreOrderIterator.iterateRecursive + r j x e m vz g l b qc g rp d o PreOrderIterator.iterateCPS + r j x e m vz g l b qc g rp d o PreOrderIterator.iterateDefCPS + r j x e m vz g l b qc g rp d o PreOrderIterator.iterate + r j x e m vz g l b qc g rp d o PreOrderIterator + + e m x g vz j qc g b d o rp l r PostOrderIterator.printRecursive + e m x g vz j qc g b d o rp l r PostOrderIterator.iterateRecursive + e m x g vz j qc g b d o rp l r PostOrderIterator.iterateCPS + e m x g vz j qc g b d o rp l r PostOrderIterator.iterateDefCPS + e m x g vz j qc g b d o rp l r PostOrderIterator.iterate + e m x g vz j qc g b d o rp l r PostOrderIterator + */ +} + +class Tree { + Tree left; + T content; + Tree right; + + Tree(Tree left, T content, Tree right) { + this.left = left; + this.content = content; + this.right = right; + } + + public static Tree generate(int maxDepth, Supplier gen, double nullProbability) { + if (nullProbability < 0 || nullProbability > 1) { + throw new IllegalArgumentException("nullProbability must be between 0 and 1"); + } + + if (maxDepth == 0) { + return null; + } + + double rand = ThreadLocalRandom.current().nextDouble(); + if (rand < nullProbability) { + return null; + } + + Tree left = generate(maxDepth - 1, gen, nullProbability); + Tree right = generate(maxDepth - 1, gen, nullProbability); + return new Tree<>(left, gen.get(), right); + } + + @Override + public String toString() { + return toStringLayout(0).toString(); + } + + private StringBuilder toStringLayout(int level) { + StringBuilder guidelines = Utils.makeGuidelines(level); + StringBuilder sb = new StringBuilder().append(guidelines).append(content).append("\n"); + if (this.left == null && this.right == null) { + return sb; + } + + if (this.left != null) { + sb.append(this.left.toStringLayout(level + 1)); + } else { + sb.append(guidelines).append("\n"); + } + + if (this.right != null) { + sb.append(this.right.toStringLayout(level + 1)); + } else { + sb.append(guidelines).append("\n"); + } + + return sb; + } +} + +class InOrderIterator implements Iterator { + + private static class Cont { + final Tree tree; + final Cont next; + + Cont(Tree tree, Cont next) { + this.tree = tree; + this.next = next; + } + } + + static void printRecursive(Tree tree) { + if (tree != null) { + printRecursive(tree.left); + Utils.printContent(tree.content); + printRecursive(tree.right); + } + } + + static void iterateRecursive(Tree tree, Consumer action) { + if (tree != null) { + iterateRecursive(tree.left, action); + action.accept(tree.content); + iterateRecursive(tree.right, action); + } + } + + static void iterateCPS(Tree tree, Consumer action, Runnable cont) { + if (tree != null) { + iterateCPS(tree.left, action, () -> { + action.accept(tree.content); + iterateCPS(tree.right, action, cont); + }); + } else { + cont.run(); + } + } + + static void iterateDefCPS(Tree tree, Consumer action, Cont cont) { + if (tree != null) { + iterateDefCPS(tree.left, action, new Cont<>(tree, cont)); + } else { + if (cont != null) { + action.accept(cont.tree.content); + iterateDefCPS(cont.tree.right, action, cont.next); + } else { + return; + } + } + } + + static void iterate(Tree tree, Consumer action, Cont cont) { + while (true) { + if (tree != null) { + cont = new Cont<>(tree, cont); + tree = tree.left; + } else { + if (cont != null) { + action.accept(cont.tree.content); + tree = cont.tree.right; + cont = cont.next; + } else { + return; + } + } + } + } + + private Tree tree; + private Stack> stack = new Stack<>(); + + InOrderIterator(Tree tree) { + this.tree = tree; + } + + @Override + public boolean hasNext() { + return tree != null || !stack.isEmpty(); + } + + @Override + public T next() { + while (hasNext()) { + if (tree != null) { + stack.push(tree); + tree = tree.left; + } else { + if (!stack.isEmpty()) { + Tree t = stack.pop(); + T content = t.content; + tree = t.right; + return content; + } + } + } + throw new NoSuchElementException(); + } +} + +class PreOrderIterator implements Iterator { + + private static class Cont { + final Tree tree; + final Cont next; + + Cont(Tree tree, Cont next) { + this.tree = tree; + this.next = next; + } + } + + static void printRecursive(Tree tree) { + if (tree != null) { + Utils.printContent(tree.content); + printRecursive(tree.left); + printRecursive(tree.right); + } + } + + static void iterateRecursive(Tree tree, Consumer action) { + if (tree != null) { + action.accept(tree.content); + iterateRecursive(tree.left, action); + iterateRecursive(tree.right, action); + } + } + + static void iterateCPS(Tree tree, Consumer action, Runnable cont) { + if (tree != null) { + action.accept(tree.content); + iterateCPS(tree.left, action, () -> iterateCPS(tree.right, action, cont)); + } else { + cont.run(); + } + } + + static void iterateDefCPS(Tree tree, Consumer action, Cont cont) { + if (tree != null) { + action.accept(tree.content); + iterateDefCPS(tree.left, action, tree.right != null ? new Cont<>(tree.right, cont) : cont); + } else { + if (cont != null) { + iterateDefCPS(cont.tree, action, cont.next); + } else { + return; + } + } + } + + static void iterate(Tree tree, Consumer action, Cont cont) { + while (true) { + if (tree != null) { + action.accept(tree.content); + if (tree.right != null) { + cont = new Cont<>(tree.right, cont); + } + tree = tree.left; + } else { + if (cont != null) { + tree = cont.tree; + cont = cont.next; + } else { + return; + } + } + } + } + + private Tree tree; + private Stack> stack = new Stack<>(); + + PreOrderIterator(Tree tree) { + this.tree = tree; + } + + @Override + public boolean hasNext() { + return tree != null || !stack.isEmpty(); + } + + @Override + public T next() { + while (hasNext()) { + if (tree != null) { + T content = tree.content; + if (tree.right != null) { + stack.push(tree.right); + } + tree = tree.left; + return content; + } else { + if (!stack.isEmpty()) { + tree = stack.pop(); + } + } + } + throw new NoSuchElementException(); + } + +} + +class PostOrderIterator implements Iterator { + + static class Cont { + final Tree tree; + final boolean isRight; + final Cont next; + + Cont(Tree tree, boolean isRight, Cont next) { + this.tree = tree; + this.next = next; + this.isRight = isRight; + } + } + + static void printRecursive(Tree tree) { + if (tree != null) { + printRecursive(tree.left); + printRecursive(tree.right); + Utils.printContent(tree.content); + } + } + + static void iterateRecursive(Tree tree, Consumer action) { + if (tree != null) { + iterateRecursive(tree.left, action); + iterateRecursive(tree.right, action); + action.accept(tree.content); + } + } + + static void iterateCPS(Tree tree, Consumer action, Runnable cont) { + if (tree != null) { + iterateCPS(tree.left, action, () -> iterateCPS(tree.right, action, () -> { + action.accept(tree.content); + cont.run(); + })); + } else { + cont.run(); + } + } + + + static void iterateDefCPS(Tree tree, Consumer action, Cont cont) { + if (tree != null) { + cont = new Cont<>(tree, false, cont); + iterateDefCPS(tree.left, action, tree.right != null ? new Cont<>(tree.right, true, cont) : cont); + } else { + if (cont != null) { + if (!cont.isRight) { + action.accept(cont.tree.content); + } + iterateDefCPS(cont.isRight ? cont.tree : null, action, cont.next); + } else { + return; + } + } + } + + static void iterate(Tree tree, Consumer action, Cont cont) { + while (true) { + if (tree != null) { + cont = new Cont<>(tree, false, cont); + cont = tree.right != null ? new Cont<>(tree.right, true, cont) : cont; + tree = tree.left; + } else { + if (cont != null) { + if (!cont.isRight) { + action.accept(cont.tree.content); + tree = null; + } else { + tree = cont.tree; + } + cont = cont.next; + } else { + return; + } + } + } + } + + private static class TreeTup { + final Tree tree; + final boolean isRight; + + public TreeTup(Tree tree, boolean isRight) { + this.tree = tree; + this.isRight = isRight; + } + } + + private Tree tree; + private Stack> stack = new Stack<>(); + + PostOrderIterator(Tree tree) { + this.tree = tree; + } + + @Override + public boolean hasNext() { + return tree != null || !stack.isEmpty(); + } + + @Override + public T next() { + while (hasNext()) { + if (tree != null) { + stack.push(new TreeTup<>(tree, false)); + if (tree.right != null) { + stack.push(new TreeTup<>(tree.right, true)); + } + tree = tree.left; + } else { + if (!stack.isEmpty()) { + TreeTup tup = stack.pop(); + if (!tup.isRight) { + T content = tup.tree.content; + tree = null; + return content; + } else { + tree = tup.tree; + } + } + } + } + throw new NoSuchElementException(); + } + +} + +class Utils { + static void printContent(T t) { + System.out.printf(t + " "); + } + + static String generateRandomAlphaString(int maxLength) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + int targetLength = random.nextInt(maxLength) + 1; + StringBuilder sb = new StringBuilder(targetLength); + for (int i = 0; i < targetLength; i++) { + sb.append((char) random.nextInt('a', 'z' + 1)); + } + return sb.toString(); + } + + static StringBuilder makeGuidelines(int times) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < times; i++) { + sb.append('│'); + } + return sb.append("├ "); + } +} \ No newline at end of file