From 48b8029cfbc7abe3ecf6f63e9553452e6ac8cf5e Mon Sep 17 00:00:00 2001 From: ganesh47 <22994026+ganesh47@users.noreply.github.com> Date: Sun, 15 Jun 2025 12:11:13 +0530 Subject: [PATCH 1/2] Add issue reference --- .../java/org/nintynine/problems/BTreeP68.java | 161 ++++++++++++++++++ .../org/nintynine/problems/BTreeP68Test.java | 62 +++++++ 2 files changed, 223 insertions(+) create mode 100644 src/main/java/org/nintynine/problems/BTreeP68.java create mode 100644 src/test/java/org/nintynine/problems/BTreeP68Test.java 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..4639d1d --- /dev/null +++ b/src/test/java/org/nintynine/problems/BTreeP68Test.java @@ -0,0 +1,62 @@ +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'))); + } +} From 5e791d25e6dba63be4e1be9012165d8018ac42f5 Mon Sep 17 00:00:00 2001 From: ganesh47 <22994026+ganesh47@users.noreply.github.com> Date: Sun, 15 Jun 2025 12:20:17 +0530 Subject: [PATCH 2/2] test: cover edge branches for BTreeP68 --- .../org/nintynine/problems/BTreeP68Test.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/test/java/org/nintynine/problems/BTreeP68Test.java b/src/test/java/org/nintynine/problems/BTreeP68Test.java index 4639d1d..13dc60b 100644 --- a/src/test/java/org/nintynine/problems/BTreeP68Test.java +++ b/src/test/java/org/nintynine/problems/BTreeP68Test.java @@ -59,4 +59,45 @@ 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()); + } }