LeetCode 501: Find Mode in Binary Search Tree Solution in Python – A Step-by-Step Guide

Imagine you’re wandering through a binary search tree (BST), where numbers are neatly arranged—smaller ones to the left, larger ones to the right—and someone asks, “Which number shows up the most?” That’s the heart of LeetCode 501: Find Mode in Binary Search Tree, an easy-to-medium problem that’s a fantastic way to practice tree traversal in Python. We’ll explore two solutions: the Best Solution, an inorder traversal that uses O(1) space by leveraging the BST’s sorted nature, and an Alternative Solution, a hash map approach that’s simpler but uses O(n) space. With detailed examples, clear code, and a friendly tone—especially for the space-saving trick—this guide will help you find those modes, whether you’re new to trees or brushing up. Let’s stroll through the BST and count some numbers!

What Is LeetCode 501: Find Mode in Binary Search Tree?

In LeetCode 501: Find Mode in Binary Search Tree, you’re given the root of a BST, and your task is to find the mode—the value(s) that appear most frequently. A BST means every node’s left subtree has smaller values, and its right subtree has larger ones. If multiple values tie for the highest frequency, return them all. For example, in a tree with values [1, 2, 2], the mode is [2]. This problem tests your ability to traverse a tree and track frequencies, building on basics from LeetCode 94: Binary Tree Inorder Traversal.

Problem Statement

  • Input: Root node of a BST (can be None).
  • Output: List of integers—the mode(s) (most frequent values).
  • Rules: If multiple modes exist, return them in any order; BST properties apply.

Constraints

  • Number of nodes: 1 to 10⁴.
  • Node values: -10⁵ to 10⁵.
  • It’s a valid BST.

Examples

  • Input: [1,null,2,2]
    • Output: [2]
    • Tree: 1 (root), 2 (right), 2 (right of 2). 2 appears twice, 1 once.
  • Input: [0]
    • Output: [0]
    • Single node, mode is 0.
  • Input: [1,1,2,null,null,2,2]
    • Output: [1,2]
    • Both 1 and 2 appear twice.

Best Solution: Inorder Traversal with O(1) Space

Why This Is the Best Solution

The inorder traversal with O(1) space is the star here because it’s memory-efficient. Since a BST’s inorder traversal yields sorted values, identical numbers appear consecutively. We can track the current value, its count, and the max count without storing everything—O(n) time, O(1) space (excluding recursion stack). It’s like counting votes as they come in, live, without a big tally sheet!

How It Works

Picture this as a walk through the tree:

  • Step 1: Traverse Inorder:
    • Visit left subtree, root, right subtree—values come in order.
  • Step 2: Track Counts:
    • Keep curr_val (current value), curr_count (its frequency), max_count (highest frequency seen), and modes (list of modes).
    • Compare each value to the previous one.
  • Step 3: Update Modes:
    • If same as previous, increment curr_count.
    • If new, check if curr_count beats or ties max_count, update modes, reset curr_count.
  • Why It Works:
    • BST ensures duplicates are adjacent in inorder.
    • No extra space beyond a few variables.

It’s like a real-time mode tracker!

Step-by-Step Example

Example: [1,null,2,2]

  • Tree:
  • ``` 1 \ 2 / 2 ```
  • Init: curr_val = None, curr_count = 0, max_count = 0, modes = [].
  • Inorder: Left (none), 1, Right (2, 2).
    • Node 1:
      • curr_val = None, now 1.
      • curr_count = 1.
      • max_count = 0 < 1, update max_count = 1, modes = [1].
    • Node 2 (left):
      • curr_val = 1, now 2.
      • Old count 1 = max_count, modes = [1].
      • curr_count = 1, max_count = 1.
    • Node 2 (right):
      • curr_val = 2, same, curr_count = 2.
      • curr_count = 2 > max_count = 1, max_count = 2, modes = [2].
  • Result: [2].

Code with Detailed Line-by-Line Explanation

Here’s the Python code, explained for beginners:

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

class Solution:
    def findMode(self, root: TreeNode) -> List[int]:
        # Variables to track state
        self.curr_val = None    # Current value
        self.curr_count = 0     # Count of current value
        self.max_count = 0      # Highest frequency
        self.modes = []         # List of modes

        def inorder(node):
            if not node:
                return

            # Left subtree
            inorder(node.left)

            # Process current node
            if node.val == self.curr_val:
                self.curr_count += 1  # Same value, increment
            else:
                # New value, check previous
                if self.curr_count > self.max_count:
                    self.max_count = self.curr_count
                    self.modes = [self.curr_val]
                elif self.curr_count == self.max_count:
                    self.modes.append(self.curr_val)
                # Reset for new value
                self.curr_val = node.val
                self.curr_count = 1

            # Right subtree
            inorder(node.right)

        # Start traversal
        inorder(root)

        # Handle the last value
        if self.curr_count > self.max_count:
            self.max_count = self.curr_count
            self.modes = [self.curr_val]
        elif self.curr_count == self.max_count:
            self.modes.append(self.curr_val)

        return self.modes
  • Lines 9-13: Class variables to track state across recursion.
  • Lines 15-16: Base case—skip null nodes.
  • Line 19: Recurse left (inorder).
  • Lines 22-31: Process node:
    • Same value: Increment curr_count.
    • New value: Update modes if curr_count beats or ties max_count, reset counters.
  • Line 34: Recurse right.
  • Lines 38-43: Handle the final value after traversal ends.
  • Time Complexity: O(n)—visit each node once.
  • Space Complexity: O(1)—just a few variables (recursion stack is O(h), where h ≤ n).

It’s like a lean, mean mode-finding machine!

Alternative Solution: Using a Hash Map

Why an Alternative Approach?

The hash map solution is simpler to understand: count every value explicitly with a dictionary, then find the max frequency. It’s O(n) time and O(n) space—less efficient in space but very straightforward, especially if you’re new to BSTs.

How It Works

Think of this as a vote counter:

  • Step 1: Traverse the tree (any order—preorder here).
  • Step 2: Use a hash map to count each value’s frequency.
  • Step 3: Find the max frequency and collect all values that match it.

It’s like tallying votes in a notebook!

Step-by-Step Example

Example: [1,null,2,2]

  • Init: count = {}, max_count = 0, modes = [].
  • Preorder: 1, 2, 2.
    • count[1] = 1, max_count = 1.
    • count[2] = 1, max_count = 1.
    • count[2] = 2, max_count = 2.
  • Find Modes:
    • count[1] = 1 < 2, skip.
    • count[2] = 2 = 2, add 2.
  • Result: [2].

Code for Hash Map Approach

class Solution:
    def findMode(self, root: TreeNode) -> List[int]:
        if not root:
            return []

        # Count frequencies
        count = {}
        def preorder(node):
            if not node:
                return
            count[node.val] = count.get(node.val, 0) + 1
            preorder(node.left)
            preorder(node.right)

        preorder(root)

        # Find max frequency
        max_count = max(count.values())

        # Collect modes
        modes = [val for val, freq in count.items() if freq == max_count]

        return modes
  • Time Complexity: O(n)—traverse and process n nodes.
  • Space Complexity: O(n)—hash map stores all values.

It’s a simple vote counter!

Comparing the Two Solutions

  • Inorder O(1) Space (Best):
    • Pros: O(1) space, leverages BST property.
    • Cons: Trickier logic.
  • Hash Map (Alternative):
    • Pros: Easy to follow, works for any tree.
    • Cons: O(n) space.

Inorder wins for efficiency in a BST!

Additional Examples and Edge Cases

  • [0]: [0]—single node.
  • [1,1,1]: [1]—all same.
  • [1,1,2,2]: [1,2]—tie.

Inorder handles them all gracefully.

Complexity Breakdown

  • Inorder: Time O(n), Space O(1).
  • Hash Map: Time O(n), Space O(n).

Inorder is the space-saver!

Key Takeaways

  • BST Trick: Sorted inorder = consecutive duplicates.
  • Space Matters: O(1) is possible—see Python Basics.
  • Hash Maps: Simple but costly.
  • Trees: Fun to traverse!

Final Thoughts: Find Those Modes!

LeetCode 501: Find Mode in Binary Search Tree in Python is a great mix of tree traversal and frequency counting. The inorder O(1) solution is a clever BST hack, while the hash map keeps it simple. Want more tree challenges? Try LeetCode 94: Binary Tree Inorder Traversal or LeetCode 235: Lowest Common Ancestor of a BST. Ready to explore? Head to Solve LeetCode 501 on LeetCode and find those modes today!