diff --git a/src/main/java/org/nintynine/problems/BTreeP68.java b/src/main/java/org/nintynine/problems/BTreeP68.java
new file mode 100644
index 0000000..f8625a1
--- /dev/null
+++ b/src/main/java/org/nintynine/problems/BTreeP68.java
@@ -0,0 +1,161 @@
+package org.nintynine.problems;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Problem P68: Preorder and inorder sequences of binary trees.
+ *
+ *
See issue #61.
+ */
+@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
+public class BTreeP68 {
+
+ private BTreeP68() {
+ // utility class
+ }
+
+ /** Simple binary tree node. */
+ public static class Node {
+ char value;
+ Node left;
+ Node right;
+
+ Node(char value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Node other)) {
+ return false;
+ }
+ return value == other.value
+ && Objects.equals(left, other.left)
+ && Objects.equals(right, other.right);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value, left, right);
+ }
+
+ @Override
+ public String toString() {
+ if (left == null && right == null) {
+ return String.valueOf(value);
+ }
+ return value
+ + "(" + (left == null ? "NIL" : left.toString())
+ + "," + (right == null ? "NIL" : right.toString()) + ")";
+ }
+ }
+
+ /** Preorder sequence for the given tree. */
+ public static List preorder(Node node) {
+ List result = new ArrayList<>();
+ preorderRec(node, result);
+ return result;
+ }
+
+ private static void preorderRec(Node node, List acc) {
+ if (node == null) {
+ return;
+ }
+ acc.add(node.value);
+ preorderRec(node.left, acc);
+ preorderRec(node.right, acc);
+ }
+
+ /** Inorder sequence for the given tree. */
+ public static List inorder(Node node) {
+ List result = new ArrayList<>();
+ inorderRec(node, result);
+ return result;
+ }
+
+ private static void inorderRec(Node node, List acc) {
+ if (node == null) {
+ return;
+ }
+ inorderRec(node.left, acc);
+ acc.add(node.value);
+ inorderRec(node.right, acc);
+ }
+
+ /**
+ * Construct a binary search tree from its preorder sequence.
+ *
+ * @param seq preorder sequence
+ * @return root of the constructed tree
+ */
+ public static Node fromPreorder(List seq) {
+ Node root = null;
+ if (seq == null) {
+ return null;
+ }
+ for (char c : seq) {
+ root = insert(root, c);
+ }
+ return root;
+ }
+
+ private static Node insert(Node node, char value) {
+ if (node == null) {
+ return new Node(value);
+ }
+ if (value < node.value) {
+ node.left = insert(node.left, value);
+ } else if (value > node.value) {
+ node.right = insert(node.right, value);
+ }
+ return node;
+ }
+
+ /**
+ * Reconstruct a tree from its preorder and inorder sequences.
+ *
+ * @param preorder preorder sequence
+ * @param inorder inorder sequence
+ * @return reconstructed tree root
+ * @throws IllegalArgumentException if inputs are invalid
+ */
+ public static Node preInTree(List preorder, List inorder) {
+ if (preorder == null || inorder == null || preorder.size() != inorder.size()) {
+ throw new IllegalArgumentException("invalid traversals");
+ }
+ Map index = new HashMap<>();
+ for (int i = 0; i < inorder.size(); i++) {
+ index.put(inorder.get(i), i);
+ }
+ Index preIdx = new Index();
+ return build(preorder, 0, inorder.size() - 1, index, preIdx);
+ }
+
+ private static Node build(
+ List preorder,
+ int inStart,
+ int inEnd,
+ Map index,
+ Index preIdx) {
+ if (inStart > inEnd) {
+ return null;
+ }
+ char rootVal = preorder.get(preIdx.pos++);
+ Node node = new Node(rootVal);
+ int inIndex = index.get(rootVal);
+ node.left = build(preorder, inStart, inIndex - 1, index, preIdx);
+ node.right = build(preorder, inIndex + 1, inEnd, index, preIdx);
+ return node;
+ }
+
+ private static class Index {
+ int pos;
+ }
+}
diff --git a/src/test/java/org/nintynine/problems/BTreeP68Test.java b/src/test/java/org/nintynine/problems/BTreeP68Test.java
new file mode 100644
index 0000000..13dc60b
--- /dev/null
+++ b/src/test/java/org/nintynine/problems/BTreeP68Test.java
@@ -0,0 +1,103 @@
+package org.nintynine.problems;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class BTreeP68Test {
+
+ private BTreeP68.Node exampleTree() {
+ BTreeP68.Node a = new BTreeP68.Node('A');
+ BTreeP68.Node b = new BTreeP68.Node('B');
+ BTreeP68.Node c = new BTreeP68.Node('C');
+ BTreeP68.Node d = new BTreeP68.Node('D');
+ BTreeP68.Node e = new BTreeP68.Node('E');
+ BTreeP68.Node f = new BTreeP68.Node('F');
+ BTreeP68.Node g = new BTreeP68.Node('G');
+ a.left = b;
+ a.right = c;
+ b.left = d;
+ b.right = e;
+ c.right = f;
+ f.left = g;
+ return a;
+ }
+
+ @Test
+ void testPreorderAndInorder() {
+ BTreeP68.Node root = exampleTree();
+ List pre = BTreeP68.preorder(root);
+ List in = BTreeP68.inorder(root);
+ assertEquals(List.of('A','B','D','E','C','F','G'), pre);
+ assertEquals(List.of('D','B','E','A','C','G','F'), in);
+ }
+
+ @Test
+ void testFromPreorder() {
+ List seq = List.of('F','C','A','E','H','G');
+ BTreeP68.Node root = BTreeP68.fromPreorder(seq);
+ assertEquals(seq, BTreeP68.preorder(root));
+ List inorder = BTreeP68.inorder(root);
+ assertEquals(List.of('A','C','E','F','G','H'), inorder); // inorder sorted
+ }
+
+ @Test
+ void testPreInTree() {
+ BTreeP68.Node root = exampleTree();
+ List pre = BTreeP68.preorder(root);
+ List in = BTreeP68.inorder(root);
+ BTreeP68.Node rebuilt = BTreeP68.preInTree(pre, in);
+ assertEquals(pre, BTreeP68.preorder(rebuilt));
+ assertEquals(in, BTreeP68.inorder(rebuilt));
+ assertEquals(root, rebuilt);
+ }
+
+ @Test
+ void testPreInTreeInvalid() {
+ assertThrows(IllegalArgumentException.class, () ->
+ BTreeP68.preInTree(List.of('A'), List.of('A','B')));
+ }
+
+ @Test
+ void testNodeEqualityAndHash() {
+ BTreeP68.Node n1 = new BTreeP68.Node('A');
+ n1.left = new BTreeP68.Node('B');
+ BTreeP68.Node n2 = new BTreeP68.Node('A');
+ n2.left = new BTreeP68.Node('B');
+ assertEquals(n1, n2); // same structure
+ assertEquals(n1, n1); // self equality
+ assertNotEquals(n1, "other");
+ assertNotEquals(n1, null);
+ assertEquals(n1.hashCode(), n2.hashCode());
+
+ assertNotEquals(n1, new BTreeP68.Node('X'));
+ BTreeP68.Node n3 = new BTreeP68.Node('A');
+ n3.left = new BTreeP68.Node('B');
+ n3.right = new BTreeP68.Node('C');
+ assertNotEquals(n1, n3);
+
+ n2.left.value = 'C';
+ assertNotEquals(n1, n2);
+ }
+
+ @Test
+ void testNodeToString() {
+ BTreeP68.Node leaf = new BTreeP68.Node('X');
+ assertEquals("X", leaf.toString());
+
+ BTreeP68.Node root = new BTreeP68.Node('A');
+ root.left = new BTreeP68.Node('B');
+ root.right = new BTreeP68.Node('C');
+ assertEquals("A(B,C)", root.toString());
+
+ BTreeP68.Node half = new BTreeP68.Node('A');
+ half.left = new BTreeP68.Node('B');
+ assertEquals("A(B,NIL)", half.toString());
+
+ BTreeP68.Node halfRight = new BTreeP68.Node('A');
+ halfRight.right = new BTreeP68.Node('B');
+ assertEquals("A(NIL,B)", halfRight.toString());
+ }
+}