Shortest path in Binary Search Tree that adds up to a given value
Problem
Given a Binary Search Tree (BST) keyed on positive integers, find the shortest path (a sequence of connected nodes) in the tree such that the sum of the keys in the path adds up to a given integer K. If no such path exists, return an appropriate message indicating that no path is found. Each path must originate from the root of the tree and may terminate at any node such that the cumulative sum equals K.
Examples
Example 1
Input:
10
/ \
7 15
/ \ / \
5 9 12 20
/ / \
3 11 13
K = 25
Output: [10, 15]
Explanation:
Path 1: 10 → 7 → 9; Cumulative Sum = 10 + 7 + 9 = 26 (Not valid as it exceeds 25).
Path 2: 10 → 15; Cumulative Sum = 10 + 15 = 25. Path Length = 2.
Path 3: 10 → 7 → 5 → 3; Cumulative Sum = 10 + 7 + 5 + 3 = 25. Path Length = 4.
We choose path 2 as it is the shortest.
Example 2
Input:
Tree: 10
/ \
6 15
/ \ / \
3 8 12 20
K = 25
Output: [10, 15]
Explanation:
The path 10 → 15 sums up to 25, which is the shortest path for the given
Solution
Method 1 - DFS
Reasoning or Intuition
Imagine we have a target number K, and we want to identify the smallest possible set of numbers (path) in a Binary Search Tree (BST) that sum up to K. The goal is to find the shortest sequence of connected nodes that adds up to this target.
Let us consider an example where K = 25 for the given BST:
10
/ \
7 15
/ \ / \
5 9 12 20
/ / \
3 11 13
There are two possible paths that sum up to 25:
- Path 1:
10 → 15- Cumulative sum = 10 + 15 = 25.
- Path length = 2.
- Path 2:
10 → 7 → 5 → 3- Cumulative sum = 10 + 7 + 5 + 3 = 25.
- Path length = 4.
Both paths achieve the target sum, but clearly, Path 1 is shorter (length = 2), so it is the preferred solution. The requirement is to find a path that achieves the desired sum while minimising the number of nodes in the sequence.
Key Observation
In a Binary Search Tree:
- Larger numbers are typically located in the right subtrees, while smaller numbers are located in the left subtrees.
- As summing larger numbers reduces the number of nodes needed to reach the target sum, exploring the right subtree preferentially can often produce shorter paths.
Consider the following principle:
- If we first explore the right subtree, we may find shorter paths summing to
Kdue to the presence of larger values. - If no valid path is found on the right, we then shift to evaluating the left subtree.
- If the cumulative sum exceeds
Kduring traversal, further exploration of that path is unnecessary, as additional nodes will only increase the sum.
Approach
To solve this problem efficiently:
- Prioritise Right Subtrees: Start investigating the right subtrees first, as they often contain larger values.
- Use Recursive Backtracking: Track the cumulative sum and the path so far, and compare results with the shortest valid path.
- Terminate Suboptimal Paths: If the cumulative sum exceeds
K, stop exploring that branch to save computational effort. - Compare and Update: Whenever a valid path sums to
K, check if its length is smaller than the shortest known path. If it is, update the result. - Fallback to Left Subtrees: If no valid path exists on the right, shift exploration to left subtrees.
Code
Java
class Solution {
// Definition for a binary tree node
static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
// Function to create duplicate nodes
public void duplicateTree(TreeNode root) {
if (root == null) {
return;
}
// Create duplicate node
TreeNode duplicate = new TreeNode(root.val);
// Attach duplicate as the left child
duplicate.left = root.left;
root.left = duplicate;
// Recursive calls for original left and right children
duplicateTree(duplicate.left);
duplicateTree(root.right);
}
// Helper function to print tree (inorder traversal)
public void print(TreeNode root) {
if (root == null) return;
print(root.left);
System.out.print(root.val + " ");
print(root.right);
}
public static void main(String[] args) {
Solution sol = new Solution();
TreeNode root = new TreeNode(2);
root.left = new TreeNode(1);
root.right = new TreeNode(3);
System.out.println("Original Tree:");
sol.print(root);
// Transform
sol.duplicateTree(root);
System.out.println("\nTransformed Tree:");
sol.print(root);
}
}
Python
class Solution:
def duplicateTree(self, root):
if root is None:
return
# Create duplicate node
duplicate = TreeNode(root.val)
# Attach duplicate as the left child
duplicate.left = root.left
root.left = duplicate
# Recursive calls for original left and right children
self.duplicateTree(duplicate.left)
self.duplicateTree(root.right)
def printTree(self, root):
# Helper function to print the tree (inorder traversal)
if root is None:
return
self.printTree(root.left)
print(root.val, end=" ")
self.printTree(root.right)
# Driver code
if __name__ == "__main__":
solution = Solution()
# Example tree
root = TreeNode(2)
root.left = TreeNode(1)
root.right = TreeNode(3)
print("Original Tree:")
solution.printTree(root)
# Transform the tree
solution.duplicateTree(root)
print("\nTransformed Tree:")
solution.printTree(root)
Complexity
-
⏰ Time complexity:
O(n). Every node in the tree is visited once, wherenis the number of nodes. -
🧺 Space complexity:
O(h). The recursion stack depth is proportional to the height of the tree, which ish(logarithmic for a balanced BST, andO(N)for a skewed tree).