Skip to content

Commit 427adbd

Browse files
committed
Add boundary traversal of binary tree
Both time complexity and space complexity are O(n)
1 parent 0d68b65 commit 427adbd

File tree

2 files changed

+274
-0
lines changed

2 files changed

+274
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package com.thealgorithms.datastructures.trees;
2+
3+
import java.util.*;
4+
5+
/**BoundaryTraversal
6+
*
7+
* Start with the Root:
8+
* Add the root node to the boundary list.
9+
* Traverse the Left Boundary (Excluding Leaf Nodes):
10+
* Move down the left side of the tree, adding each non-leaf node to the boundary list.
11+
* If a node has a left child, go left; otherwise, go right.
12+
* Visit All Leaf Nodes:
13+
* Traverse the tree and add all leaf nodes to the boundary list, from left to right.
14+
* Traverse the Right Boundary (Excluding Leaf Nodes) in Reverse Order:
15+
* Move up the right side of the tree, adding each non-leaf node to a temporary list.
16+
* If a node has a right child, go right; otherwise, go left.
17+
* Reverse the temporary list and add it to the boundary list.
18+
* Combine and Output:
19+
* The final boundary list contains the root, left boundary, leaf nodes, and reversed right boundary in that order.
20+
*/
21+
public final class BoundaryTraversal {
22+
private BoundaryTraversal() {}
23+
24+
// Main function for boundary traversal, returns a list of boundary nodes in order
25+
public static List<Integer> boundaryTraversal(BinaryTree.Node root) {
26+
List<Integer> result = new ArrayList<>();
27+
if (root == null) {
28+
return result;
29+
}
30+
31+
// Add root node if it's not a leaf node
32+
if (!isLeaf(root)) {
33+
result.add(root.data);
34+
}
35+
36+
// Add left boundary
37+
addLeftBoundary(root, result);
38+
39+
// Add leaf nodes
40+
addLeaves(root, result);
41+
42+
// Add right boundary
43+
addRightBoundary(root, result);
44+
45+
return result;
46+
}
47+
48+
// Adds the left boundary, including nodes that have no left child but have a right child
49+
private static void addLeftBoundary(BinaryTree.Node node, List<Integer> result) {
50+
BinaryTree.Node cur = node.left;
51+
52+
// If there is no left child but there is a right child, treat the right child as part of the left boundary
53+
if (cur == null && node.right != null) {
54+
cur = node.right;
55+
}
56+
57+
while (cur != null) {
58+
if (!isLeaf(cur)) {
59+
result.add(cur.data); // Add non-leaf nodes to result
60+
}
61+
if (cur.left != null) {
62+
cur = cur.left; // Move to the left child
63+
} else if (cur.right != null) {
64+
cur = cur.right; // If left child is null, move to the right child
65+
} else {
66+
break; // Stop if there are no children
67+
}
68+
}
69+
}
70+
71+
// Adds leaf nodes (nodes without children)
72+
private static void addLeaves(BinaryTree.Node node, List<Integer> result) {
73+
if (node == null) {
74+
return;
75+
}
76+
if (isLeaf(node)) {
77+
result.add(node.data); // Add leaf node
78+
} else {
79+
addLeaves(node.left, result); // Recur for left subtree
80+
addLeaves(node.right, result); // Recur for right subtree
81+
}
82+
}
83+
84+
// Adds the right boundary, excluding leaf nodes
85+
private static void addRightBoundary(BinaryTree.Node node, List<Integer> result) {
86+
BinaryTree.Node cur = node.right;
87+
List<Integer> temp = new ArrayList<>();
88+
89+
// If no right boundary is present and there is no left subtree, skip
90+
if (cur != null && node.left == null) {
91+
return;
92+
}
93+
while (cur != null) {
94+
if (!isLeaf(cur)) {
95+
temp.add(cur.data); // Store non-leaf nodes temporarily
96+
}
97+
if (cur.right != null) {
98+
cur = cur.right; // Move to the right child
99+
} else if (cur.left != null) {
100+
cur = cur.left; // If right child is null, move to the left child
101+
} else {
102+
break; // Stop if there are no children
103+
}
104+
}
105+
106+
// Add the right boundary nodes in reverse order
107+
for (int i = temp.size() - 1; i >= 0; i--) {
108+
result.add(temp.get(i));
109+
}
110+
}
111+
112+
// Checks if a node is a leaf node
113+
private static boolean isLeaf(BinaryTree.Node node) {
114+
return (node.left == null && node.right == null);
115+
}
116+
117+
// Iterative boundary traversal
118+
public static List<Integer> iterativeBoundaryTraversal(BinaryTree.Node root) {
119+
List<Integer> result = new ArrayList<>();
120+
if (root == null) {
121+
return result;
122+
}
123+
124+
// Add root node if it's not a leaf node
125+
if (!isLeaf(root)) {
126+
result.add(root.data);
127+
}
128+
129+
// Handle the left boundary
130+
BinaryTree.Node cur = root.left;
131+
if (cur == null && root.right != null) {
132+
cur = root.right;
133+
}
134+
while (cur != null) {
135+
if (!isLeaf(cur)) {
136+
result.add(cur.data); // Add non-leaf nodes to result
137+
}
138+
cur = (cur.left != null) ? cur.left : cur.right; // Prioritize left child, move to right if left is null
139+
}
140+
141+
// Add leaf nodes
142+
addLeaves(root, result);
143+
144+
// Handle the right boundary using a stack (reverse order)
145+
cur = root.right;
146+
Deque<Integer> stack = new LinkedList<>();
147+
if (cur != null && root.left == null) {
148+
return result;
149+
}
150+
while (cur != null) {
151+
if (!isLeaf(cur)) {
152+
stack.push(cur.data); // Temporarily store right boundary nodes in a stack
153+
}
154+
cur = (cur.right != null) ? cur.right : cur.left; // Prioritize right child, move to left if right is null
155+
}
156+
157+
// Add the right boundary nodes from the stack to maintain the correct order
158+
while (!stack.isEmpty()) {
159+
result.add(stack.pop());
160+
}
161+
return result;
162+
}
163+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package com.thealgorithms.datastructures.trees;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import java.util.Collections;
6+
import java.util.List;
7+
import org.junit.jupiter.api.Test;
8+
9+
/**
10+
*
11+
*/
12+
public class BoundaryTraversalTest {
13+
14+
15+
@Test
16+
public void testNullRoot() {
17+
assertEquals(Collections.emptyList(), BoundaryTraversal.boundaryTraversal(null));
18+
assertEquals(Collections.emptyList(), BoundaryTraversal.iterativeBoundaryTraversal(null));
19+
}
20+
21+
@Test
22+
public void testSingleNodeTree() {
23+
final BinaryTree.Node root = new BinaryTree.Node(1);
24+
25+
List<Integer> expected = List.of(1);
26+
27+
assertEquals(expected, BoundaryTraversal.boundaryTraversal(root));
28+
assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root));
29+
}
30+
/*
31+
1
32+
/ \
33+
2 3
34+
/ \ / \
35+
4 5 6 7
36+
37+
*/
38+
@Test
39+
public void testCompleteBinaryTree() {
40+
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {
41+
1, 2, 3, 4, 5, 6, 7
42+
});
43+
44+
List<Integer> expected = List.of(1, 2, 4, 5, 6, 7, 3);
45+
46+
assertEquals(expected, BoundaryTraversal.boundaryTraversal(root));
47+
assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root));
48+
}
49+
/*
50+
1
51+
/ \
52+
2 7
53+
/ \
54+
3 8
55+
\ /
56+
4 9
57+
/ \
58+
5 6
59+
/ \
60+
10 11
61+
*/
62+
@Test
63+
public void testBoundaryTraversal() {
64+
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {
65+
1, 2, 7, 3, null, null, 8, null, 4, 9, null, 5, 6, 10, 11
66+
});
67+
68+
List<Integer> expected = List.of(1, 2, 3, 4, 5, 6, 10, 11, 9, 8, 7);
69+
70+
assertEquals(expected, BoundaryTraversal.boundaryTraversal(root));
71+
assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root));
72+
}
73+
/*
74+
1
75+
/
76+
2
77+
/
78+
3
79+
/
80+
4
81+
*/
82+
@Test
83+
public void testLeftSkewedTree() {
84+
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1,2,null,3,null,4,null});
85+
86+
List<Integer> expected = List.of(1,2,3,4);
87+
88+
assertEquals(expected, BoundaryTraversal.boundaryTraversal(root));
89+
assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root));
90+
}
91+
/*
92+
5
93+
\
94+
6
95+
\
96+
7
97+
\
98+
8
99+
*/
100+
@Test
101+
public void testRightSkewedTree() {
102+
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {5,null,6,null,7,null,8});
103+
104+
List<Integer> expected = List.of(5, 6, 7,8);
105+
106+
assertEquals(expected, BoundaryTraversal.boundaryTraversal(root));
107+
assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root));
108+
}
109+
110+
111+
}

0 commit comments

Comments
 (0)