diff --git a/java/Trees/BalancedBinaryTreeValidation.java b/java/Trees/BalancedBinaryTreeValidation.java new file mode 100644 index 0000000..ac4287c --- /dev/null +++ b/java/Trees/BalancedBinaryTreeValidation.java @@ -0,0 +1,40 @@ +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class BalancedBinaryTreeValidation { + public boolean balancedBinaryTreeValidation(TreeNode root) { + return getHeightImbalance(root) != -1; + } + + private int getHeightImbalance(TreeNode node) { + // Base case: if the node is null, its height is 0. + if (node == null) { + return 0; + } + // Recursively get the height of the left and right subtrees. If + // either subtree is imbalanced, propagate -1 up the tree. + int leftHeight = getHeightImbalance(node.left); + int rightHeight = getHeightImbalance(node.right); + if (leftHeight == -1 || rightHeight == -1) { + return -1; + } + // If the current node's subtree is imbalanced + // (height difference > 1), return -1. + if (Math.abs(leftHeight - rightHeight) > 1) { + return -1; + } + // Return the height of the current subtree. + return 1 + Math.max(leftHeight, rightHeight); + } +} diff --git a/java/Trees/BinarySearchTreeValidation.java b/java/Trees/BinarySearchTreeValidation.java new file mode 100644 index 0000000..3aab5ed --- /dev/null +++ b/java/Trees/BinarySearchTreeValidation.java @@ -0,0 +1,39 @@ +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class BinarySearchTreeValidation { + public boolean binarySearchTreeValidation(TreeNode root) { + // Start validation at the root node. The root node can contain any + // value, so set the initial lower and upper bounds to null. + return isWithinBounds(root, null, null); + } + + private boolean isWithinBounds(TreeNode node, Integer lowerBound, Integer upperBound) { + // Base case: if the node is null, it satisfies the BST condition. + if (node == null) { + return true; + } + // If the current node's value is not within the valid bounds, this + // tree is not a valid BST. + if (lowerBound != null && node.val <= lowerBound || upperBound != null && upperBound <= node.val) { + return false; + } + // If the left subtree isn't a BST, this tree isn't a BST. + if (!isWithinBounds(node.left, lowerBound, node.val)) { + return false; + } + // Otherwise, return true if the right subtree is also a BST. + return isWithinBounds(node.right, node.val, upperBound); + } +} diff --git a/java/Trees/BinaryTreeColumns.java b/java/Trees/BinaryTreeColumns.java new file mode 100644 index 0000000..f22dbc6 --- /dev/null +++ b/java/Trees/BinaryTreeColumns.java @@ -0,0 +1,68 @@ +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class BinaryTreeColumns { + public class Pair { + TreeNode node; + int column; + public Pair(TreeNode node, int column) { + this.node = node; + this.column = column; + } + } + + public List> binaryTreeColumns(TreeNode root) { + if (root == null) { + return new ArrayList<>(); + } + Map> columnMap = new HashMap<>(); + int leftmostColumn, rightmostColumn; + leftmostColumn = rightmostColumn = 0; + Queue queue = new ArrayDeque<>(); + queue.offer(new Pair(root, 0)); + while (!queue.isEmpty()) { + Pair pair = queue.poll(); + TreeNode node = pair.node; + int column = pair.column; + if (node != null) { + // Add the current node's value to its corresponding list in the hash + // map. + List columnList = columnMap.getOrDefault(column, new ArrayList<>()); + columnList.add(node.val); + columnMap.put(column, columnList); + leftmostColumn = Math.min(leftmostColumn, column); + rightmostColumn = Math.max(rightmostColumn, column); + // Add the current node's children to the queue with their respective + // column ids. + queue.offer(new Pair(node.left, column - 1)); + queue.offer(new Pair(node.right, column + 1)); + } + } + // Construct the output list by collecting values from each column in the hash + // map in the correct order. + List> res = new ArrayList<>(); + for (int i = leftmostColumn; i <= rightmostColumn; i++) { + List column = columnMap.get(i); + res.add(column); + } + return res; + } +} diff --git a/java/Trees/BinaryTreeSymmetry.java b/java/Trees/BinaryTreeSymmetry.java new file mode 100644 index 0000000..75ca523 --- /dev/null +++ b/java/Trees/BinaryTreeSymmetry.java @@ -0,0 +1,44 @@ +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class BinaryTreeSymmetry { + public boolean binaryTreeSymmetry(TreeNode root) { + if (root == null) { + return true; + } + return compareTrees(root.left, root.right); + } + + private boolean compareTrees(TreeNode node1, TreeNode node2) { + // Base case: if both nodes are null, they're symmetric. + if (node1 == null && node2 == null) { + return true; + } + // If one node is null and the other isn't, they aren't symmetric. + if (node1 == null || node2 == null) { + return false; + } + // If the values of the current nodes don't match, trees aren't symmetric. + if (node1.val != node2.val) { + return false; + } + // Compare the 'node1's left subtree with 'node2's right subtree. If these + // aren't symmetric, the whole tree is not symmetric. + if (!compareTrees(node1.left, node2.right)) { + return false; + } + // Compare the 'node1's right subtree with 'node2's left subtree. + return compareTrees(node1.right, node2.left); + } +} diff --git a/java/Trees/BuildBinaryTree.java b/java/Trees/BuildBinaryTree.java new file mode 100644 index 0000000..d25af53 --- /dev/null +++ b/java/Trees/BuildBinaryTree.java @@ -0,0 +1,50 @@ +import java.util.HashMap; +import java.util.Map; + +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class BuildBinaryTree { + int preorderIndex = 0; + Map inorderIndexesMap = new HashMap<>(); + + public TreeNode buildBinaryTree(int[] preorder, int[] inorder) { + // Populate the hash map with the inorder values and their indexes. + for (int i = 0; i < inorder.length; i++) { + inorderIndexesMap.put(inorder[i], i); + } + // Build the tree and return its root node. + return buildSubtree(0, inorder.length - 1, preorder, inorder); + } + + private TreeNode buildSubtree(int left, int right, int[] preorder, int[] inorder) { + // Base case: if no elements are in this range, return None. + if (left > right) { + return null; + } + int val = preorder[preorderIndex]; + // Set 'inorderIndex' to the index of the same value pointed at by + // 'preorderIndex'. + int inorderIndex = inorderIndexesMap.get(val); + TreeNode node = new TreeNode(val); + // Advance 'preorderIndex' so it points to the value of the next + // node to be created. + preorderIndex++; + // Build the left and right subtrees and connect them to the current + // node. + node.left = buildSubtree(left, inorderIndex - 1, preorder, inorder); + node.right = buildSubtree(inorderIndex + 1, right, preorder, inorder); + return node; + } +} diff --git a/java/Trees/InvertBinaryTreeIterative.java b/java/Trees/InvertBinaryTreeIterative.java new file mode 100644 index 0000000..2d9d6f9 --- /dev/null +++ b/java/Trees/InvertBinaryTreeIterative.java @@ -0,0 +1,40 @@ +import java.util.Stack; + +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class InvertBinaryTreeIterative { + public TreeNode invertBinaryTreeIterative(TreeNode root) { + if (root == null) { + return null; + } + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + // Swap the left and right subtrees of the current node. + TreeNode tmp = node.left; + node.left = node.right; + node.right = tmp; + // Push the left and right subtrees onto the stack. + if (node.left != null) { + stack.push(node.left); + } + if (node.right != null) { + stack.push(node.right); + } + } + return root; + } +} diff --git a/java/Trees/InvertBinaryTreeRecursive.java b/java/Trees/InvertBinaryTreeRecursive.java new file mode 100644 index 0000000..ad2bdb2 --- /dev/null +++ b/java/Trees/InvertBinaryTreeRecursive.java @@ -0,0 +1,30 @@ +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class InvertBinaryTreeRecursive { + public TreeNode invertBinaryTreeRecursive(TreeNode root) { + // Base case: If the node is null, there's nothing to invert. + if (root == null) { + return root; + } + // Swap the left and right subtrees of the current node. + TreeNode tmp = root.left; + root.left = root.right; + root.right = tmp; + // Recursively invert the left and right subtrees. + invertBinaryTreeRecursive(root.left); + invertBinaryTreeRecursive(root.right); + return root; + } +} diff --git a/java/Trees/KthSmallestNumberInBSTIterative.java b/java/Trees/KthSmallestNumberInBSTIterative.java new file mode 100644 index 0000000..c9f4db0 --- /dev/null +++ b/java/Trees/KthSmallestNumberInBSTIterative.java @@ -0,0 +1,41 @@ +import java.util.Stack; + +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class KthSmallestNumberInBSTIterative { + public int kthSmallestNumberInBSTIterative(TreeNode root, int k) { + Stack stack = new Stack<>(); + TreeNode node = root; + while (!stack.isEmpty() || node != null) { + // Move to the leftmost node and add nodes to the stack as we go so they + // can be processed in future iterations. + while (node != null) { + stack.push(node); + node = node.left; + } + // Pop the top node from the stack to process it, and decrement 'k'. + node = stack.pop(); + k--; + // If we have processed 'k' nodes, return the value of the 'k'th smallest + // node. + if (k == 0) { + return node.val; + } + // Move to the right subtree. + node = node.right; + } + return -1; + } +} diff --git a/java/Trees/KthSmallestNumberInBSTRecursive.java b/java/Trees/KthSmallestNumberInBSTRecursive.java new file mode 100644 index 0000000..dbead79 --- /dev/null +++ b/java/Trees/KthSmallestNumberInBSTRecursive.java @@ -0,0 +1,34 @@ +import java.util.ArrayList; +import java.util.List; + +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class KthSmallestNumberInBSTRecursive { + public int kthSmallestNumberInBSTRecursive(TreeNode root, int k) { + List sortedList = new ArrayList<>(); + inorder(root, sortedList); + return sortedList.get(k - 1); + } + + // Inorder traversal function to attain a sorted list of nodes from the BST. + private void inorder(TreeNode node, List sortedList) { + if (node == null) { + return; + } + inorder(node.left, sortedList); + sortedList.add(node.val); + inorder(node.right, sortedList); + } +} diff --git a/java/Trees/LowestCommonAncestor.java b/java/Trees/LowestCommonAncestor.java new file mode 100644 index 0000000..65b5b56 --- /dev/null +++ b/java/Trees/LowestCommonAncestor.java @@ -0,0 +1,41 @@ +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class LowestCommonAncestor { + TreeNode lca; + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + dfs(root, p, q); + return lca; + } + + private boolean dfs(TreeNode node, TreeNode p, TreeNode q) { + // Base case: a null node is neither 'p' nor 'q'. + if (node == null) { + return false; + } + int nodeIsPOrQ = (node == p || node == q) ? 1 : 0; + // Recursively determine if the left and right subtrees contain 'p' + // or 'q'. + int leftContainsPOrQ = dfs(node.left, p, q) ? 1 : 0; + int rightContainsPOrQ = dfs(node.right, p, q) ? 1 : 0; + // If two of the above three variables are true, the current node is + // the LCA. + if (nodeIsPOrQ + leftContainsPOrQ + rightContainsPOrQ == 2) { + lca = node; + } + // Return true if the current subtree contains 'p' or 'q'. + return nodeIsPOrQ + leftContainsPOrQ + rightContainsPOrQ > 0; + } +} diff --git a/java/Trees/MaximumPathSum.java b/java/Trees/MaximumPathSum.java new file mode 100644 index 0000000..e9fd0a7 --- /dev/null +++ b/java/Trees/MaximumPathSum.java @@ -0,0 +1,39 @@ +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class MaximumPathSum { + int maxSum = Integer.MIN_VALUE; + + public int maxPathSum(TreeNode root) { + maxPathSumHelper(root); + return maxSum; + } + + private int maxPathSumHelper(TreeNode node) { + // Base case: null nodes have no path sum. + if (node == null) { + return 0; + } + // Collect the maximum gain we can attain from the left and right + // subtrees, setting them to 0 if they're negative. + int leftSum = Math.max(maxPathSumHelper(node.left), 0); + int rightSum = Math.max(maxPathSumHelper(node.right), 0); + // Update the overall maximum path sum if the current path sum is + // larger. + maxSum = Math.max(maxSum, node.val + leftSum + rightSum); + // Return the maximum sum of a single, continuous path with the + // current node as an endpoint. + return node.val + Math.max(leftSum, rightSum); + } +} diff --git a/java/Trees/RightmostNodesOfABinaryTree.java b/java/Trees/RightmostNodesOfABinaryTree.java new file mode 100644 index 0000000..9fadf6e --- /dev/null +++ b/java/Trees/RightmostNodesOfABinaryTree.java @@ -0,0 +1,48 @@ +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; + +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class RightmostNodesOfABinaryTree { + public List rightmostNodesOfABinaryTree(TreeNode root) { + if (root == null) { + return new ArrayList<>(); + } + List res = new ArrayList<>(); + Queue queue = new ArrayDeque<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int levelSize = queue.size(); + // Add all the non-null child nodes of the current level to the + // queue. + for (int i = 0; i < levelSize; i++) { + TreeNode node = queue.poll(); + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + // Record this level's last node to the result array. + if (i == levelSize - 1) { + res.add(node.val); + } + } + } + return res; + } +} diff --git a/java/Trees/SerializeAndDeserializeABinaryTree.java b/java/Trees/SerializeAndDeserializeABinaryTree.java new file mode 100644 index 0000000..4e59fd6 --- /dev/null +++ b/java/Trees/SerializeAndDeserializeABinaryTree.java @@ -0,0 +1,64 @@ +import java.lang.StringBuilder; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Queue; + +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class SerializeAndDeserializeABinaryTree { + public String serialize(TreeNode root) { + // Perform a preorder traversal to add node values to a list, then convert the + // list to a string. + StringBuilder serializedList = new StringBuilder(); + preorderSerialize(root, serializedList); + return serializedList.toString(); + } + + // Helper function to perform serialization through preorder traversal. + private void preorderSerialize(TreeNode node, StringBuilder serializedList) { + // Base case: mark null nodes as '#'. + if (node == null) { + serializedList.append("#").append(","); + return; + } + // Preorder traversal processes the current node first, then the left and right + // children. + serializedList.append(node.val).append(","); + preorderSerialize(node.left, serializedList); + preorderSerialize(node.right, serializedList); + } + + public TreeNode deserialize(String data) { + // Obtain the node values by splitting the string using the comma delimiter. + Queue nodeValues = new LinkedList<>(); + nodeValues.addAll(Arrays.asList(data.split(","))); + return buildTree(nodeValues); + } + + // Helper function to construct the tree using preorder traversal. + private TreeNode buildTree(Queue values) { + String val = values.poll(); + // Base case: '#' indicates a null node. + if (val.equals("#")) { + return null; + } + // Use preorder traversal processes the current node first, then the left and + // right children. + TreeNode node = new TreeNode(Integer.valueOf(val)); + node.left = buildTree(values); + node.right = buildTree(values); + return node; + } +} diff --git a/java/Trees/WidestBinaryTreeLevel.java b/java/Trees/WidestBinaryTreeLevel.java new file mode 100644 index 0000000..8ab0225 --- /dev/null +++ b/java/Trees/WidestBinaryTreeLevel.java @@ -0,0 +1,60 @@ +import java.util.ArrayDeque; +import java.util.Queue; + +import DS.TreeNode; + +/* + // Definition of TreeNode: + class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int val) { + this.val = val; + } + } + */ + +public class WidestBinaryTreeLevel { + public class Pair { + TreeNode node; + int index; + public Pair(TreeNode node, int index) { + this.node = node; + this.index = index; + } + } + + public int widestBinaryTreeLevel(TreeNode root) { + if (root == null) { + return 0; + } + int maxWidth = 0; + Queue queue = new ArrayDeque<>(); + queue.offer(new Pair(root, 0)); // Stores (node, index) pairs. + while (!queue.isEmpty()) { + int levelSize = queue.size(); + // Set the 'leftmostIndex' to the index of the first node in + // this level. Start 'rightmostIndex' at the same point as + // 'leftmostIndex' and update it as we traverse the level, + // eventually positioning it at the last node. + int leftmostIndex = queue.peek().index; + int rightmostIndex = leftmostIndex; + // Process all nodes at the current level. + for (int j = 0; j < levelSize; j++) { + Pair pair = queue.poll(); + TreeNode node = pair.node; + int i = pair.index; + if (node.left != null) { + queue.offer(new Pair(node.left, 2 * i + 1)); + } + if (node.right != null) { + queue.offer(new Pair(node.right, 2 * i + 2)); + } + rightmostIndex = i; + } + maxWidth = Math.max(maxWidth, rightmostIndex - leftmostIndex + 1); + } + return maxWidth; + } +}