Algorithm Tips (3) – Building Suitable Data Structure

The more LeetCode questions we practice, the more rules we find. We can split all questions to serval categories, such as Stack, Map/HashMap, Dynamic Programming, etc. It looks good to use these existing data structure to improve the solution, but sometimes, we still need to build our own suitable data structure to fit some questions’ special scenario.

Today I take “Binary Tree Maximum Path Sum” as an example. (Later, I will enhance this post by adding more examples.)

Question:

Given a binary tree, find the maximum path sum. For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root.

Analysis:

For any node, it has two cases, one is single side(not circle), the other is go-through-circle. We need to record the two cases for each node and then to be provided to parent’s node to evaluate. So it is quite clear to solve the problem. Now you can use int[] which contains 2 elements to code it, but we all know it is not the good one and hard to read. The better solution is to build our own data structure to describe the two cases clearly.

private class ResultType {
  int singlePath, maxPath;
  ResultType(int singlePath, int maxPath) {
    this.singlePath = singlePath;
    this.maxPath = maxPath;
  }
}

Here we conclude above two cases to two parameters: singlePath is to record the maximum path for single side(not circle) and maxPath to record the maximum value for the current node, including circle case and non-circle case. So the whole codes are here:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *   int val;
 *   TreeNode left;
 *   TreeNode right;
 *   TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
   private class ResultType {
     int singlePath, maxPath;
     ResultType(int singlePath, int maxPath) {
       this.singlePath = singlePath;
       this.maxPath = maxPath;
     }
   }
   public int maxPathSum(TreeNode root) {
     ResultType res = helper(root);
     return res.maxPath;
   }
 
   public ResultType helper(TreeNode root) {
     if (root == null) 
       return new ResultType(Integer.MIN_VALUE, Integer.MIN_VALUE);
     ResultType left = helper(root.left);
     ResultType right = helper(root.right);
 
     int singlePath = Math.max(0, 
             Math.max(left.singlePath, right.singlePath)) + root.val;
     int maxPath = Math.max(left.maxPath, right.maxPath);
     maxPath = Math.max(maxPath, 
             Math.max(left.singlePath, 0)+
                      Math.max(right.singlePath, 0)+root.val);
     return new ResultType(singlePath, maxPath);
   }
}

We see parent’s singlePath = Max(their children’s singlePath + root value) and parent’s maxPath = Max( children’s maxPath,  children’s singlePath + root value).

Meanwhile, we comparing singlePath with zero, to determine whether to involve it or not.

Another clever point is to return Integer.MIN_VALUE, not zero for the empty node. This is very clever to solve the case, like only one node with a negative value. Because in this case, this negative value is still the maximum value for this node.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s