Bea.AI coding blog
  • Tree
    • Tree Traverse
    • Iteration based traverse
    • Typical problems and solutions
      • Max width of binary tree
      • Binary Tree & BST Serialize & Deserialize
      • Lowest common ancestor (LCA) for Binary Tree and BST
      • Subproblem-based Approach for Resolving Max Value on Trees
      • Path sum III
  • Graph
  • Data structure
    • Java data structure
  • Algorithm general templates
    • Tree
    • Trie
    • Graph
  • Java Algorithm Tips and Tricks
    • Java concurrent list
  • Coding patterns & Strategies
  • Coding patterns & template
    • 1. Binary Search
    • 2. Two Pointer
    • 3. Sliding Window
    • 4. Backtracking
    • 5. DFS
    • 6. BFS
    • 7. Stack
    • 8. Heap
    • 9. Prefix Sum
    • 10. Linked List
    • 11. BST
    • 12. Line sweep
    • 14. Tree
    • 15. Graph
    • 16. Bit manipulation
    • 17. Matrix
    • 18. Monotonic Stack
    • 19. Sorting
    • 20. Union Find
    • 21. Trie
    • 22. Dynamic programming
    • 23. Customized Data Structure
  • Code interview steps
  • Leetcode
    • Tree
      • Traversal
      • Construction and Conversion
      • Search and Validation
      • Manipulation and Modification
      • Special Trees
      • Miscellaneous
    • Dynamic programing
      • Classic DP Problems
      • Sequence/Array DP
      • Matrix DP
      • Knapsack Problems
      • String DP
      • Tree/Graph DP
      • Optimization Problems
      • Combinatorial DP
    • DFS
      • Graph Traversal
      • Path Finding
      • Tree Traversal
      • Backtracking
      • Topological Sorting
      • Traversal Order
      • Dynamic Programming with DFS
      • Connected Components
    • BFS
      • Graph
      • Tree
      • Matrix
      • String
      • Others
    • Two points
      • Array
      • String
      • Linked List
      • Sorting
      • Others
    • LinkedList
      • Basic Operations
      • Cycle Detection and Handling
      • Intersection and Union
      • Partitioning and Merging
      • Palindrome
      • Miscellaneous
    • Backtracking
    • Binary Search
    • Union Find
    • Trie
    • Sort
    • Heap
    • Randomness
    • Topological sort
    • Stack
    • HashTable
    • Sliding Window
  • Blind 75
    • Array
    • Binary
    • Dynamic Programming
    • Graph
    • Interval
    • LinkedList
    • Matrix
    • String
    • Tree
    • Heap
  • Companies
    • Apple
Powered by GitBook
On this page
  • 1. Search in Binary Search Tree (LeetCode #700)
  • 2. Validate Binary Search Tree (LeetCode #98)
  • 3. Lowest Common Ancestor of a Binary Tree (LeetCode #236)
  • 4. Lowest Common Ancestor of a Binary Search Tree (LeetCode #235)
  • 5. Path Sum (LeetCode #112)
  • 6. Path Sum II (LeetCode #113)
  1. Leetcode
  2. Tree

Search and Validation

1. Search in Binary Search Tree (LeetCode #700)

  1. Problem Description: The problem "Search in Binary Search Tree" asks us to find a specific value in a binary search tree and return the corresponding node if found. The problem can be found on LeetCode at the following link: https://leetcode.com/problems/search-in-a-binary-search-tree/.

  2. Optimized Java Code: Below is the optimized Java code for the problem "Search in Binary Search Tree". Detailed comments have been added to explain the algorithm and steps taken:

// Definition for a binary tree node
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { 
        val = x; 
    }
}

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        // Base case: if the root is null or the root value matches the target value
        if (root == null || root.val == val) {
            return root;
        }
        
        // If the target value is less than the root value, search in the left subtree
        if (val < root.val) {
            return searchBST(root.left, val);
        }
        
        // If the target value is greater than the root value, search in the right subtree
        return searchBST(root.right, val);
    }
}
  1. Algorithm Explanation: In this problem, we are given a binary search tree (BST), which is a binary tree where the value of each node is greater than or equal to the values in its left subtree and less than or equal to the values in its right subtree.

To solve this problem, we can utilize the BST property to our advantage. We start from the root of the tree and compare the target value with the current node's value. Based on the comparison, we can narrow down our search to either the left or right subtree.

  • If the target value is equal to the current node's value, we have found our desired node and return it.

  • If the target value is less than the current node's value, we recursively search in the left subtree.

  • If the target value is greater than the current node's value, we recursively search in the right subtree.

By following this approach, the search operation will take place efficiently in logarithmic time complexity, as we eliminate half of the search space at each step.

During the interview process, it is crucial to demonstrate an understanding of binary search trees and their properties. By explaining the algorithm step-by-step, highlighting the use of recursion, and discussing the logarithmic time complexity, you can showcase your problem-solving skills and understanding of data structures.

2. Validate Binary Search Tree (LeetCode #98)

  1. Problem Description and Link: The problem is to validate if a given binary tree is a valid binary search tree (BST). A valid BST is defined as follows:

  • The left subtree of a node contains only nodes with keys less than the node's key.

  • The right subtree of a node contains only nodes with keys greater than the node's key.

  • Both the left and right subtrees must also be valid BSTs.

Link to the problem on LeetCode: https://leetcode.com/problems/validate-binary-search-tree/

  1. Optimized Java Code with Comments:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isValidBST(TreeNode root) {
        return isValidBST(root, null, null);
    }
    
    private boolean isValidBST(TreeNode node, Integer min, Integer max) {
        if (node == null) {
            return true;
        }
        
        // Check if the current node's value violates the BST property
        if (min != null && node.val <= min || max != null && node.val >= max) {
            return false;
        }
        
        // Recursively validate the left and right subtrees
        return isValidBST(node.left, min, node.val) && isValidBST(node.right, node.val, max);
    }
}
  1. Algorithm and Approach Explanation: To solve this problem, we can use the concept of recursion and divide and conquer. Here's a step-by-step breakdown of the approach:

  • We define a helper function isValidBST(TreeNode node, Integer min, Integer max), which checks if the given node is a valid BST node within the specified range (min and max).

  • If the current node is null, it means we have reached the end of a subtree and we return true.

  • We then check if the current node.val violates the BST property. If min is not null and node.val is less than or equal to min, or if max is not null and node.val is greater than or equal to max, it means the current node is invalid and we return false.

  • Finally, we recursively call the isValidBST function for the left and right subtrees. We update the min limit for the left subtree as the current node's value (since all nodes in the left subtree must be less than the current node), and we update the max limit for the right subtree as the current node's value (since all nodes in the right subtree must be greater than the current node).

  • The result of validating the left and right subtrees is combined using the && (logical AND) operator, ensuring that both subtrees are valid BSTs for the given node.

  • Finally, we return the result of the main isValidBST function call, passing the root of the tree, null as the minimum limit, and null as the maximum limit.

During an interview, it is important to discuss the algorithm and approach with the interviewer and explain why it solves the problem efficiently. Additionally, discussing time and space complexity analysis can demonstrate a deeper understanding of the problem and its solution.

3. Lowest Common Ancestor of a Binary Tree (LeetCode #236)

  1. Problem Description: The problem "Lowest Common Ancestor of a Binary Tree" asks us to find the lowest common ancestor (LCA) of two given nodes in a binary tree. The LCA of two nodes n1 and n2 is the shared ancestor that is located farthest from the root and still contains both nodes.

  2. Here is the optimized Java code for the problem "Lowest Common Ancestor of a Binary Tree (LeetCode #236)" with detailed comments:

/*
Problem Link: https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/

Problem Description:
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.

Definition of Lowest Common Ancestor (LCA):
The LCA of two nodes p and q in a binary tree is the lowest node that has both p and q as descendants (where we allow a node to be a descendant of itself).

Solution Approach:
To find the LCA, we can use a recursive approach. Starting from the root, we check if the given nodes n1 and n2 are present in the left subtree and right subtree of the current node.

If both nodes are found on separate sides (left and right), the current node is the LCA.
If both nodes are found on one side (either left or right), we continue searching for the LCA in that subtree.
If only one of the nodes is found, we return that node as the LCA.
If none of the nodes are found, we return null.

The main idea is to make use of the tree structure and the recursive property of trees. We check both the left and right subtrees to find the LCA. By making use of the recursive function, we check if the nodes are found in the left and right subtrees and return the appropriate LCA based on the conditions mentioned above.
*/

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    
    TreeNode(int x) {
        val = x;
    }
}

public class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // If root is null or root equals to either of the nodes, return root
        if (root == null || root == p || root == q) {
            return root;
        }
        
        // Recursively find the LCA in left and right subtrees
        TreeNode leftLCA = lowestCommonAncestor(root.left, p, q);
        TreeNode rightLCA = lowestCommonAncestor(root.right, p, q);
        
        // If nodes are found on separate sides (left and right), return the current node as LCA
        if (leftLCA != null && rightLCA != null) {
            return root;
        }
        
        // If only one node is found, return that node as LCA
        if (leftLCA != null) {
            return leftLCA;
        }
        
        if (rightLCA != null) {
            return rightLCA;
        }
        
        // If none of the nodes are found, return null
        return null;
    }
}
  1. Algorithm Explanation and Interview Thinking Process:

The problem is to find the lowest common ancestor (LCA) of two given nodes in a binary tree. To solve this problem efficiently, we can use a recursive approach that utilizes the tree structure.

During the interview process, you can explain your approach as follows:

  • Start with the given root node of the binary tree.

  • Check if the root node is null or it matches either of the given nodes n1 or n2. If so, return the root node as it is the common ancestor.

  • Recursively find the LCA in the left and right subtrees by making recursive calls to the same function on the left and right children of the current node.

  • If both the left and right subtrees return non-null values, it means that each of the nodes n1 and n2 is found on different sides (left and right) of the current node. In this case, the current node is the LCA, so we return it.

  • If only one of the left or right subtrees returns a non-null value, it means that both n1 and n2 are present on the same side (either left or right) of the current node. In this case, we continue searching for the LCA in that subtree.

  • If none of the nodes are found in the current subtree (both left and right subtrees return null), we return null as there is no LCA.

By explaining your approach and thought process in this way, the interviewer will be able to understand your understanding of the problem, your problem-solving approach, and your ability to explain the solution clearly and concisely.

4. Lowest Common Ancestor of a Binary Search Tree (LeetCode #235)

  1. Problem Description:

    • Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.

    • The lowest common ancestor is defined between two nodes p and q as the lowest node in the tree that has both p and q as descendants (where we allow a node to be a descendant of itself).

  2. Optimized Java Code:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        int parentVal = root.val;
        int pVal = p.val;
        int qVal = q.val;
        
        // If both p and q are lesser than the parent, LCA must be in the left subtree.
        if (pVal < parentVal && qVal < parentVal) {
            return lowestCommonAncestor(root.left, p, q);
        }
        
        // If both p and q are greater than the parent, LCA must be in the right subtree.
        else if (pVal > parentVal && qVal > parentVal) {
            return lowestCommonAncestor(root.right, p, q);
        }
        
        // If either p or q is equal to the parent, or if one is less and the other is greater, then the parent itself is the LCA.
        else {
            return root;
        }
    }
}
  1. Algorithm and Interview Process Discussion:

    • To solve this problem, we utilize the properties of a binary search tree (BST) and recursion.

    • The key observation is that if both nodes p and q are lesser than the current root node, their lowest common ancestor (LCA) lies in the left subtree. Similarly, if both nodes are greater than the current root node, the LCA lies in the right subtree.

    • We can use this property to recursively search for the LCA in the appropriate subtree.

    • The base case of the recursion is when we reach a node where p and q are on opposite sides (one is lesser and the other is greater than the root node). In this case, the LCA must be the current root node itself.

    • During an interview, it is crucial to understand the problem requirements and constraints. Make sure to ask clarifying questions if something is not clear.

    • To approach the problem, start by considering a simple example and try to manually identify the LCA. Observe any patterns or conditions that hold true for the LCA in a BST.

    • Communicate your thought process with the interviewer, explaining the properties of a BST and how you can leverage them to find the LCA efficiently.

    • Encode the recursive algorithm outlined above into code, making sure to handle the base case and the conditions for choosing the appropriate subtree.

    • Finally, test your code with provided test cases and edge cases to ensure correctness.

5. Path Sum (LeetCode #112)

  1. Optimized Java Code with Detailed Comments:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return false;
        }
        
        // Check if the current node is a leaf node.
        if (root.left == null && root.right == null && targetSum - root.val == 0) {
            return true;
        }
        
        // Recursively check for path sums in the left and right subtrees.
        return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
    }
}
  1. Detailed Description of the Algorithm and How to Think Through It During Interview Process:

The problem requires us to determine if there exists a root-to-leaf path in a binary tree, such that the sum of the values along the path is equal to a given target sum.

To approach this problem, we start by checking if the given binary tree is empty (i.e., root is null). If the tree is empty, there cannot be any path with the given sum, so we return false.

Next, we check if the current node is a leaf node (i.e., both its left and right children are null) and if the target sum minus the node's value is 0. If both conditions are satisfied, we return true, as we have found a path with the given sum.

If the current node is not a leaf node and the target sum minus the node's value is not 0, we recursively check for paths in the left and right subtrees. This is done by calling the hasPathSum() function with the left and right child nodes of the current node, along with the updated target sum (target sum minus the current node's value).

By performing the recursive calls, we will traverse all possible paths in the binary tree and check if any of them sum up to the target sum. If we find such a path, the function will return true. Otherwise, it will return false.

During an interview, it is essential to analyze the problem requirements and constraints, understand the input and output format, and design an efficient and optimized solution. Additionally, writing code with proper variable naming, indentation, and clear comments is crucial to improve code readability and maintainability.

6. Path Sum II (LeetCode #113)

  1. Problem Description: The problem "Path Sum II" on LeetCode (#113) involves finding all root-to-leaf paths in a binary tree which add up to a given sum.

  2. Optimized Java Code with Detailed Comments:

import java.util.ArrayList;
import java.util.List;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode(int val) {
        this.val = val;
    }
}

public class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        List<List<Integer>> result = new ArrayList<>();
        List<Integer> currentPath = new ArrayList<>();
        pathSumHelper(root, sum, currentPath, result);
        return result;
    }

    private void pathSumHelper(TreeNode root, int sum, List<Integer> currentPath, List<List<Integer>> result) {
        if (root == null) {
            return;
        }

        // Add current node's value to the current path
        currentPath.add(root.val);

        // If current node is a leaf and its value is equal to remaining sum, add the path to the result
        if (root.left == null && root.right == null && root.val == sum) {
            result.add(new ArrayList<>(currentPath));
        }

        // Recursively check left and right subtrees
        pathSumHelper(root.left, sum - root.val, currentPath, result);
        pathSumHelper(root.right, sum - root.val, currentPath, result);

        // Remove current node's value from the current path before backtracking
        currentPath.remove(currentPath.size() - 1);
    }
}
  1. Algorithm Explanation and Interview Tips:

    • The algorithm recursively traverses the binary tree, keeping track of the current path and the running sum.

    • At each node, we add the node's value to the current path and check if it's a leaf node. If the leaf node's value matches the remaining sum, we add the current path to the result list.

    • We then recursively call the helper function for the left and right subtrees, updating the sum for each call by subtracting the current node's value.

    • After the recursive calls, we remove the current node's value from the current path to backtrack.

    • The time complexity of this algorithm is O(N), where N is the number of nodes in the binary tree.

    • During an interview, make sure to discuss potential edge cases such as an empty tree or a zero sum. Walk through the code with specific examples to demonstrate the algorithm's behavior.

    • Emphasize the importance of keeping track of the current path and backtracking appropriately after exploring the left and right subtrees.

Given the root of a binary tree and an integer targetSum, return the number of paths where the sum of the values along the path equals targetSum.

The path does not need to start or end at the root or a leaf, but it must go downwards (i.e., traveling only from parent nodes to child nodes).

public class Solution {
    public int pathSum(TreeNode root, int sum) {
        if (root == null) {
            return 0;
        }
        
        return pathSumFrom(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum);
    }
    
    private int pathSumFrom(TreeNode node, int sum) {
        if (node == null) {
            return 0;
        }
        
        int count = 0;
        if (node.val == sum) {
            count++;
        }
        
        count += pathSumFrom(node.left, sum - node.val);
        count += pathSumFrom(node.right, sum - node.val);
        
        return count;
    }
}

PreviousConstruction and ConversionNextManipulation and Modification

Last updated 1 year ago

LeetCode Link:

Problem Description: The problem is named "Path Sum" and is available on LeetCode with the problem ID #112. The link to the problem is:

Problem link:

Lowest Common Ancestor of a Binary Search Tree
Path Sum
Path Sum II
Path Sum III