Largest Binary Search Tree (BST), by Apple

Here is today's problem from Daily Coding Problem:

This problem was asked by Apple.
Given a tree, find the largest tree/subtree that is a BST.
Given a tree, return the size of the largest tree/subtree that is a BST.

Take a look at the tree below. Every node by itself is a BST of course, but I have highlighted some other BSTs in this tree. The largest one has double lines covering it, and has 5 nodes:


I first tried one approach de-serializing using in-order traversal so that I'd end up with a sorted list, and from there look at the BSTs, but it really doesn't quite work. An idea that worked was to do a post-order traversal using the following logic:

  • The function returns the largest BST with the current node as the root
  • Call for the left (a)
  • Call for the right (b)
  • The minimum number for the current node should be 1
  • But if the left node is smaller, add it (from (a))
  • But if the right node is larger, add it (from (b))
  • Check whether the current value is greater than the max (and set max accordingly)
  • Return the current

Running against the input above does return 5:


Code is down below and on GitHub, here: https://github.com/marcelodebarros/dailycodingproblem/blob/master/DailyCodingProblem12162018.cs

Cheers, Marcelo

public int LargestBST(Tree tree)
{
 int max = 0;
 LargestBST(tree, ref max);
 return max;
}

private int LargestBST(Tree tree, ref int max)
{
 if (tree == null) return 0;

 int maxLeft = LargestBST(tree.left, ref max);
 int maxRight = LargestBST(tree.right, ref max);

 int current = 1;
 if (tree.left != null && tree.val > tree.left.val)
  current += maxLeft;
 if (tree.right != null && tree.val <= tree.right.val)
  current += maxRight;

 max = (current > max) ? current : max;
 return current;
}

Comments

  1. This one is my most favorite algorithm. Thank you for sharing. I just write a few comments based on my experience. 1. Your algorithm time complexity is O(N), N is total number of nodes in tree, bottom up since post order is used in the traversal, most time efficient. Not top down. 2. Every node is considered for the candidate as a root of binary search tree. Very good brute force idea to search maximum one. 3. Base case is correct, one node is BST with value 1; Count nodes not edges. 4. a tip to share, this algorithm is similar to Leetcode: longest univalue path.

    ReplyDelete
  2. Thanks Jianmin! I was checking and I actually tried https://leetcode.com/problems/longest-univalue-path/description/ before, but my solution was wrong... I'll take a look again with this new hint :)

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Thank you for the kindness to share your personal experience with my favorite algorithm: longest univalue path. I like to share more if you do not mind. We may bring more people to join the discussion on the most favorite tree algorithms. It is marked easy on Leetcode.com but it is hard than some hard level tree algorithms.

    Because I used this algorithm to interview over 15 interviewees on interviewing.io last 10 months over 100 mock interviews. I learned so many solutions through so many talents.

    One more sharing

    The intern did share his solution when I interviewed him on interviewing.io recently. I just like to share the link, it was fun to work with young talent and stay open. And the end of mock interview, the interviewee told me that he is a facebook intern.

    10/29/18 at 10:04 PM Lexical Lguana interviewed by Platinum Burrito on interviewing.io

    Second sharing

    What I learned most is to interview another intern recently but he did not choose to showcase the interview of his approach. At the end of mock interview, he told me that he is a facebook intern. I will write the blog to share with you later.

    My blog will be ready in any time soon, here is my plan to write here:
    http://juliachencoding.blogspot.com/2018/12/case-study-longest-univalue-path.html

    Third sharing
    I learned how to write a recursive solution directly instead of working around using brute force solution you provided. I learned from one of senior engineer as an interviewer as well.

    I learned all kinds of ideas to solve it, you name it.

    ReplyDelete
    Replies
    1. Fantastic, Jianmin, thanks a lot! Because of your hint I was able to go back to leetcode and successfully implemented that solution - indeed, uses the same concept, but I agree with you it is not as easy as the other easy problems there. Glad that you're enjoying this blog, I'll sure follow yours too! :) Here is my solution:

      public class Solution
      {
      public int LongestUnivaluePath(TreeNode root)
      {
      int maxThruCurrent = 0;
      int maxCurrent = 0;
      int maxGlobal = 0;
      LongestUnivaluePath(root, ref maxThruCurrent, ref maxCurrent, ref maxGlobal);
      return maxGlobal;
      }

      private void LongestUnivaluePath(TreeNode root,
      ref int maxThruCurrent,
      ref int maxCurrent,
      ref int maxGlobal)
      {
      if (root == null) return;

      int maxThruLeft = 0;
      int maxLeft = 0;
      LongestUnivaluePath(root.left, ref maxThruLeft, ref maxLeft, ref maxGlobal);

      int maxThruRight = 0;
      int maxRight = 0;
      LongestUnivaluePath(root.right, ref maxThruRight, ref maxRight, ref maxGlobal);

      if (root.left != null && root.val == root.left.val)
      {
      maxThruCurrent += (1 + maxLeft);
      maxCurrent = Math.Max(maxCurrent, 1 + maxLeft);
      }
      if (root.right != null && root.val == root.right.val)
      {
      maxThruCurrent += (1 + maxRight);
      maxCurrent = Math.Max(maxCurrent, 1 + maxRight);
      }

      maxGlobal = Math.Max(maxGlobal, Math.Max(maxThruCurrent, maxCurrent));
      }
      }

      Delete
  5. Thank you for sharing longest univalue path. I played with your solution in the above a few times, it is hard for me to come out the good review, but I did something to share with you.

    The variable maxThruLeft is declared and calculated but it is no use in your code. Same as maxThruRight variable.

    If we use maxThruCurrent variable and apply it to every node in the tree using post order, then we can get longest univalue path. No need to use third variable called maxGlobal.

    The following is the code I rewrote and just provide you a second opinion. The code passes online judge.

    public int LongestUnivaluePath(TreeNode root)
    {
    int maxThruCurrent = 0;
    int maxCurrent = 0;

    LongestUnivaluePath(root, ref maxThruCurrent, ref maxCurrent);
    return maxThruCurrent;
    }

    private static void LongestUnivaluePath(TreeNode root,
    ref int maxThruCurrent,
    ref int maxCurrent)
    {
    if (root == null) return;

    int maxLeft = 0;
    LongestUnivaluePath(root.left, ref maxThruCurrent, ref maxLeft);

    int maxRight = 0;
    LongestUnivaluePath(root.right, ref maxThruCurrent, ref maxRight);

    int currentMaxThruCurrent = 0;
    if (root.left != null && root.val == root.left.val)
    {
    currentMaxThruCurrent += (1 + maxLeft);
    maxCurrent = Math.Max(maxCurrent, 1 + maxLeft);
    }

    if (root.right != null && root.val == root.right.val)
    {
    currentMaxThruCurrent += (1 + maxRight);
    maxCurrent = Math.Max(maxCurrent, 1 + maxRight);
    }

    maxThruCurrent = Math.Max(currentMaxThruCurrent, maxThruCurrent);
    }

    ReplyDelete

Post a Comment

Popular posts from this blog

Advent of Code - Day 6, 2024: BFS and FSM

Advent of Code - Day 7, 2024: Backtracking and Eval

Golang vs. C#: performance characteristics (simple case study)