Skip to content

Commit

Permalink
Added Tress traverse()
Browse files Browse the repository at this point in the history
  • Loading branch information
minborg committed Jan 27, 2015
1 parent 3419203 commit 12b3058
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 6 deletions.
68 changes: 62 additions & 6 deletions src/main/java/com/speedment/util/Trees.java
@@ -1,7 +1,11 @@
package com.speedment.util; package com.speedment.util;


import java.util.ArrayDeque;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Queue;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;


/** /**
Expand All @@ -18,6 +22,17 @@ public static enum Order {
FORWARD, BACKWARD; FORWARD, BACKWARD;
} }


/**
* The TraversalType can control how a tree is walked.
*
* http://en.wikipedia.org/wiki/Tree_traversal
*
*/
public static enum TraversalOrder {

DEPTH_FIRST_PRE, /*DEPTH_FIRST_IN, Supported only for left/right trees*/ DEPTH_FIRST_POST, BREADTH_FIRST;
}

public static <T> Stream<T> walk(T leaf, Function<T, T> traverser) { public static <T> Stream<T> walk(T leaf, Function<T, T> traverser) {
return walk(leaf, traverser, Order.FORWARD, Stream.builder()).build(); return walk(leaf, traverser, Order.FORWARD, Stream.builder()).build();
} }
Expand All @@ -34,6 +49,14 @@ public static <T> Stream<T> walkOptional(T leaf, Function<T, Optional<T>> traver
return walkOptional(leaf, traverser, order, Stream.builder()).build(); return walkOptional(leaf, traverser, order, Stream.builder()).build();
} }


public static <T> Stream<T> traverse(T leaf, Function<T, Stream<T>> traverser, TraversalOrder traversalOrder) {
if (traversalOrder == TraversalOrder.BREADTH_FIRST) {
return traverseBredthFirst(leaf, traverser, Stream.builder()).build();
} else {
return traverse(leaf, traverser, traversalOrder, Stream.builder()).build();
}
}

// //
// Private support methods // Private support methods
// //
Expand All @@ -48,18 +71,51 @@ private static <T> Stream.Builder<T> walkOptional(T leaf, Function<T, Optional<T
return builder; return builder;
} }


private static <T> Stream.Builder<T> walk(T leaf, Function<T, T> traverser, Order order, Stream.Builder<T> list) { private static <T> Stream.Builder<T> walk(T leaf, Function<T, T> traverser, Order order, Stream.Builder<T> builder) {
if (order == Order.FORWARD) { if (order == Order.FORWARD) {
list.add(leaf); builder.add(leaf);
} }
T next = traverser.apply(leaf); final T next = traverser.apply(leaf);
if (next != null) { if (next != null) {
walk(next, traverser, order, list); walk(next, traverser, order, builder);
} }
if (order == Order.BACKWARD) { if (order == Order.BACKWARD) {
list.add(leaf); builder.add(leaf);
}
return builder;
}

private static <T> Stream.Builder<T> traverse(T leaf, Function<T, Stream<T>> traverser, TraversalOrder traversalOrder, Stream.Builder<T> builder) {
if (leaf == null) {
return builder;
} }
return list; if (traversalOrder == TraversalOrder.DEPTH_FIRST_PRE) {
builder.add(leaf);
}
final Stream<T> next = traverser.apply(leaf);
if (next != null) {
next.filter(Objects::nonNull).forEach((T n) -> {
traverse(n, traverser, traversalOrder, builder);
});
}
if (traversalOrder == TraversalOrder.DEPTH_FIRST_POST) {
builder.add(leaf);
}
return builder;
}

private static <T> Stream.Builder<T> traverseBredthFirst(T leaf, Function<T, Stream<T>> traverser, Stream.Builder<T> builder) {
if (leaf == null) {
return builder;
}
final Queue<T> q = new ArrayDeque<>();
q.add(leaf);
while (!q.isEmpty()) {
final T node = q.poll();
builder.add(node);
traverser.apply(node).filter(Objects::nonNull).forEach(q::add);
}
return builder;
} }


} }
126 changes: 126 additions & 0 deletions src/test/java/com/speedment/util/TreesTest.java
@@ -0,0 +1,126 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.speedment.util;

import static com.speedment.util.Trees.TraversalOrder.BREADTH_FIRST;
import static com.speedment.util.Trees.TraversalOrder.DEPTH_FIRST_POST;
import static com.speedment.util.Trees.TraversalOrder.DEPTH_FIRST_PRE;
import java.util.Objects;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.After;
import org.junit.AfterClass;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
*
* @author pemi
*/
public class TreesTest {

public TreesTest() {
}

@BeforeClass
public static void setUpClass() {
}

@AfterClass
public static void tearDownClass() {
}

@Before
public void setUp() {
}

@After
public void tearDown() {
}

/**
* Test of traverse method, of class Trees.
*/
@Test
public void testTraverse() {
System.out.println("traverse");

// http://en.wikipedia.org/wiki/Tree_traversal
final Node f = new Node("F",
new Node("B",
new Node("A"),
new Node("D",
new Node("C"),
new Node("E")
)
),
new Node("G",
null,
new Node("I",
new Node("H"),
null))
);
test(f, DEPTH_FIRST_PRE, "F,B,A,D,C,E,G,I,H");
test(f, DEPTH_FIRST_POST, "A,C,E,D,B,H,I,G,F");
test(f, BREADTH_FIRST, "F,B,G,A,D,I,C,E,H");

Stream.of(Trees.TraversalOrder.values()).forEach(to -> {
test(null, to, "");
});

}

private static void test(Node f, Trees.TraversalOrder traversalOrder, String expectedResult) {
final String result = Trees.traverse(f, Node::stream, traversalOrder).map(Node::getName).collect(JOINING_COMAS);
System.out.printf("%-20s: %s\n", traversalOrder, result);
assertEquals(expectedResult, result);
}

public class Node {

private final Node left;
private final Node right;
private final String name;

public Node(final String name, final Node left, final Node right) {
this.left = left;
this.right = right;
this.name = name;
}

public Node(String name) {
this(name, null, null);
}

@Override
public String toString() {
return getName();
}

public Node getLeft() {
return left;
}

public Node getRight() {
return right;
}

public String getName() {
return name;
}

public Stream<Node> stream() {
return Stream.of(getLeft(), getRight()).filter(Objects::nonNull);
}

}

public static final Collector<CharSequence, ?, String> JOINING_COMAS = Collectors.joining(",");

}

0 comments on commit 12b3058

Please sign in to comment.