Taro Logo

Longest Univalue Path

Medium
Google logo
Google
1 view
Topics:
TreesRecursion

Given the root of a binary tree, return the length of the longest path, where each node in the path has the same value. This path may or may not pass through the root. The length of the path between two nodes is represented by the number of edges between them.

For example:

Consider the following binary tree:

      5
     / \
    4   5
   / \   \
  1   1   5

In this case, the longest path with the same value is the path consisting of the two '5' nodes on the right side, connected by one edge. So the function should return 2 if we consider the path 5 -> 5 that includes the root node and also the path 1 -> 4 -> 4 -> 1 has node values other than 1.

As another example, consider the binary tree:

      1
     / \
    4   5
   / \   \
  4   4   5

Here, the longest path with the same value is the path between the two '4' nodes on the left side, connected by one edge. So, the function should return 2 because 4 -> 4 is the longest path (or 5 -> 5 since they are the same length). The root node has no bearing.

Your task is to write a function that efficiently determines the length of the longest path with the same value in a given binary tree.

Constraints:

  • The number of nodes in the tree is in the range `[0, 10^4]``.
  • -1000 <= Node.val <= 1000
  • The depth of the tree will not exceed 1000.

Solution


Naive Solution: Brute Force

A brute force approach to solve this problem would involve exploring all possible paths in the binary tree and checking if the nodes along each path have the same value. We would then keep track of the longest such path.

This approach is highly inefficient because it explores many redundant paths, leading to exponential time complexity. It's not practical for trees with a significant number of nodes.

Big O Analysis

  • Time Complexity: O(N^2) where N is the number of nodes, due to the need to explore all possible paths.
  • Space Complexity: O(H), where H is the height of the tree, for the recursion stack.

Optimal Solution: Depth-First Search (DFS)

A more efficient solution can be achieved using Depth-First Search (DFS). We can traverse the tree recursively, and for each node, determine the length of the longest path with the same value that passes through it. The key idea is to calculate the length of the path extending from the left and right subtrees.

Algorithm

  1. Initialize a variable longest_path to 0. This will store the maximum length found so far.
  2. Define a recursive function dfs(node) that returns the length of the longest path starting from the given node downwards, where all nodes have the same value as the current node.
  3. In the dfs(node) function:
    • If the node is null, return 0.
    • Recursively call dfs on the left and right children.
    • If the left child exists and has the same value as the current node, update the left path length. Otherwise, set it to 0.
    • Do the same for the right child.
    • Update longest_path with the maximum of longest_path and the sum of the left and right path lengths.
    • Return the maximum of the left and right path lengths plus 1 (to account for the edge to the parent).
  4. Call the dfs function on the root node.
  5. Return the longest_path.

Edge Cases

  • Empty Tree: If the root is null, the longest path is 0.
  • Single Node Tree: If the tree has only one node, the longest path is 0 (as the problem asks for edges between nodes).
  • Nodes with different values: The algorithm should correctly handle trees where nodes have different values.

Code (Python)

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    def longestUnivaluePath(self, root: TreeNode) -> int:
        self.longest_path = 0

        def dfs(node):
            if not node:
                return 0

            left_length = dfs(node.left)
            right_length = dfs(node.right)

            left_path = 0
            right_path = 0

            if node.left and node.left.val == node.val:
                left_path = left_length + 1
            if node.right and node.right.val == node.val:
                right_path = right_length + 1

            self.longest_path = max(self.longest_path, left_path + right_path)

            return max(left_path, right_path)

        dfs(root)
        return self.longest_path

Big O Analysis

  • Time Complexity: O(N), where N is the number of nodes in the tree, as each node is visited once.
  • Space Complexity: O(H), where H is the height of the tree, due to the recursive call stack. In the worst-case scenario (skewed tree), H can be equal to N. In the best-case scenario (balanced tree), H is log(N).