Skip to content

Add boundary traversal of binary tree #5639

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package com.thealgorithms.datastructures.trees;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

/**
* BoundaryTraversal
* <p>
* Start with the Root:
* Add the root node to the boundary list.
* Traverse the Left Boundary (Excluding Leaf Nodes):
* Move down the left side of the tree, adding each non-leaf node to the boundary list.
* If a node has a left child, go left; otherwise, go right.
* Visit All Leaf Nodes:
* Traverse the tree and add all leaf nodes to the boundary list, from left to right.
* Traverse the Right Boundary (Excluding Leaf Nodes) in Reverse Order:
* Move up the right side of the tree, adding each non-leaf node to a temporary list.
* If a node has a right child, go right; otherwise, go left.
* Reverse the temporary list and add it to the boundary list.
* Combine and Output:
* The final boundary list contains the root, left boundary, leaf nodes, and reversed right boundary in that order.
*/
public final class BoundaryTraversal {
private BoundaryTraversal() {
}

// Main function for boundary traversal, returns a list of boundary nodes in order
public static List<Integer> boundaryTraversal(BinaryTree.Node root) {
List<Integer> result = new ArrayList<>();
if (root == null) {
return result;
}

// Add root node if it's not a leaf node
if (!isLeaf(root)) {
result.add(root.data);
}

// Add left boundary
addLeftBoundary(root, result);

// Add leaf nodes
addLeaves(root, result);

// Add right boundary
addRightBoundary(root, result);

return result;
}

// Adds the left boundary, including nodes that have no left child but have a right child
private static void addLeftBoundary(BinaryTree.Node node, List<Integer> result) {
BinaryTree.Node cur = node.left;

// If there is no left child but there is a right child, treat the right child as part of the left boundary
if (cur == null && node.right != null) {
cur = node.right;
}

while (cur != null) {
if (!isLeaf(cur)) {
result.add(cur.data); // Add non-leaf nodes to result
}
if (cur.left != null) {
cur = cur.left; // Move to the left child
} else if (cur.right != null) {
cur = cur.right; // If left child is null, move to the right child
} else {
break; // Stop if there are no children
}
}
}

// Adds leaf nodes (nodes without children)
private static void addLeaves(BinaryTree.Node node, List<Integer> result) {
if (node == null) {
return;
}
if (isLeaf(node)) {
result.add(node.data); // Add leaf node
} else {
addLeaves(node.left, result); // Recur for left subtree
addLeaves(node.right, result); // Recur for right subtree
}
}

// Adds the right boundary, excluding leaf nodes
private static void addRightBoundary(BinaryTree.Node node, List<Integer> result) {
BinaryTree.Node cur = node.right;
List<Integer> temp = new ArrayList<>();

// If no right boundary is present and there is no left subtree, skip
if (cur != null && node.left == null) {
return;
}
while (cur != null) {
if (!isLeaf(cur)) {
temp.add(cur.data); // Store non-leaf nodes temporarily
}
if (cur.right != null) {
cur = cur.right; // Move to the right child
} else if (cur.left != null) {
cur = cur.left; // If right child is null, move to the left child
} else {
break; // Stop if there are no children
}
}

// Add the right boundary nodes in reverse order
for (int i = temp.size() - 1; i >= 0; i--) {
result.add(temp.get(i));
}
}

// Checks if a node is a leaf node
private static boolean isLeaf(BinaryTree.Node node) {
return node.left == null && node.right == null;
}

// Iterative boundary traversal
public static List<Integer> iterativeBoundaryTraversal(BinaryTree.Node root) {
List<Integer> result = new ArrayList<>();
if (root == null) {
return result;
}

// Add root node if it's not a leaf node
if (!isLeaf(root)) {
result.add(root.data);
}

// Handle the left boundary
BinaryTree.Node cur = root.left;
if (cur == null && root.right != null) {
cur = root.right;
}
while (cur != null) {
if (!isLeaf(cur)) {
result.add(cur.data); // Add non-leaf nodes to result
}
cur = (cur.left != null) ? cur.left : cur.right; // Prioritize left child, move to right if left is null
}

// Add leaf nodes
addLeaves(root, result);

// Handle the right boundary using a stack (reverse order)
cur = root.right;
Deque<Integer> stack = new LinkedList<>();
if (cur != null && root.left == null) {
return result;
}
while (cur != null) {
if (!isLeaf(cur)) {
stack.push(cur.data); // Temporarily store right boundary nodes in a stack
}
cur = (cur.right != null) ? cur.right : cur.left; // Prioritize right child, move to left if right is null
}

// Add the right boundary nodes from the stack to maintain the correct order
while (!stack.isEmpty()) {
result.add(stack.pop());
}
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.thealgorithms.datastructures.trees;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Test;

/**
*
*/
public class BoundaryTraversalTest {

@Test
public void testNullRoot() {
assertEquals(Collections.emptyList(), BoundaryTraversal.boundaryTraversal(null));
assertEquals(Collections.emptyList(), BoundaryTraversal.iterativeBoundaryTraversal(null));
}

@Test
public void testSingleNodeTree() {
final BinaryTree.Node root = new BinaryTree.Node(1);

List<Integer> expected = List.of(1);

assertEquals(expected, BoundaryTraversal.boundaryTraversal(root));
assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root));
}

/*
1
/ \
2 3
/ \ / \
4 5 6 7

*/
@Test
public void testCompleteBinaryTree() {
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7});

List<Integer> expected = List.of(1, 2, 4, 5, 6, 7, 3);

assertEquals(expected, BoundaryTraversal.boundaryTraversal(root));
assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root));
}

/*
1
/ \
2 7
/ \
3 8
\ /
4 9
/ \
5 6
/ \
10 11
*/
@Test
public void testBoundaryTraversal() {
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 7, 3, null, null, 8, null, 4, 9, null, 5, 6, 10, 11});

List<Integer> expected = List.of(1, 2, 3, 4, 5, 6, 10, 11, 9, 8, 7);

assertEquals(expected, BoundaryTraversal.boundaryTraversal(root));
assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root));
}

/*
1
/
2
/
3
/
4
*/
@Test
public void testLeftSkewedTree() {
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, null, 3, null, 4, null});

List<Integer> expected = List.of(1, 2, 3, 4);

assertEquals(expected, BoundaryTraversal.boundaryTraversal(root));
assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root));
}

/*
5
\
6
\
7
\
8
*/
@Test
public void testRightSkewedTree() {
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {5, null, 6, null, 7, null, 8});

List<Integer> expected = List.of(5, 6, 7, 8);

assertEquals(expected, BoundaryTraversal.boundaryTraversal(root));
assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root));
}
}