Skip to content
Home » Finding Min and Max Values in a BST: Java Solution

Finding Min and Max Values in a BST: Java Solution

Introduction

Hey there, welcome back to “Art of Coding”! If you’ve been following our journey through the world of Binary Search Trees (BSTs), you’re in for a treat. Today, we’re rolling up our sleeves to tackle another common challenge: finding the minimum and maximum elements in a BST, using our favorite language, Java.

We’re going to break it down for you, step by step, so you can not only understand but also apply these concepts in your coding adventures.

So, grab a cup of your favorite coffee, and let’s get started!

Understanding the BST Structure for Min/Max Values

Before we jump into the coding specifics, let’s quickly revisit the structure of a Binary Search Tree. A BST is a rooted binary tree whose each node has up to two children, and they’re arranged according to one simple rule: for any given node, all values in its left subtree are smaller, and all values in its right subtree are larger.

Why does this matter for finding the minimum and maximum values? Well, thanks to this rule, the minimum value in a BST is the leftmost node, and the maximum value is the rightmost node.

Take a look at the image we’ve included here. You’ll notice the node with the value of 3 is colored differently. That’s because it’s our leftmost node—our minimum value. On the other side, the node with the value of 18 stands out, rightly taking its place as the rightmost node—our maximum value.

Illustration of a Binary Search Tree highlighting the minimum value at node 3 and the maximum value at node 18.
Fig. 1 - Identifying the Min and Max in a BST

Java Implementation: Finding Min and Max in a BST

To facilitate the process of finding the minimum and maximum values, we have two classes: Node and TreeUtil. The Node class represents each node in the tree, encapsulating the element value, as well as references to the left and right children.

Here’s a peek at the Node class:

public class Node {

    private int element;
    private Node left;
    private Node right;

    public Node(int data) {
        this.element = data;
    }

    public void add(int newValue) {
        if (newValue < this.element) {
            // If newValue is less than the current node's value, go left
            if (this.left == null) {
                // If left child is null, create a new node and assign it
                this.left = new Node(newValue);
            } else {
                // If left child exists, recursively call add on the left subtree
                this.left.add(newValue);
            }
        } else {
            // If newValue is greater than or equal to the current node's value, go right
            if (this.right == null) {
                // If right child is null, create a new node and assign it
                this.right = new Node(newValue);
            } else {
                // If right child exists, recursively call add on the right subtree
                this.right.add(newValue);
            }
        }
    }

    public int getElement() {
        return element;
    }

    public Node getLeft() {
        return left;
    }

    public Node getRight() {
        return right;
    }
}

The add method within the Node class is a perfect showcase of recursion in action. Every time you want to insert a new value, this method ensures it finds its proper place, adhering strictly to the BST ordering rules. 

If you’re intrigued by the insertion process and how this method contributes to the integrity of the BST structure, you’ll definitely enjoy our in-depth look at BST insertions. Check out our previous article, BST Insertion & In-Order Traversal: A Beginner’s Guide.

With our nodes ready, we turn to the TreeUtil class, which houses our findMinimum and findMaximum methods:

public class TreeUtil {
    public static int findMinimum(Node root) {
        if (root == null) {
            throw new IllegalArgumentException("Root is null");
        }

        Node current = root;
        while (current.getLeft() != null) {
            current = current.getLeft();
        }

        return current.getElement();
    }

    public static int findMaximum(Node root) {
        if (root == null) {
            throw new IllegalArgumentException("Root is null");
        }

        Node current = root;
        while (current.getRight() != null) {
            current = current.getRight();
        }

        return current.getElement();
    }
}

These methods are static, meaning you can call them without creating an instance of TreeUtil. They start at the root and traverse the tree down to the leftmost node for the minimum, and the rightmost node for the maximum, neatly exploiting the BST’s ordered properties.

Should the tree be empty, the findMinimum and findMaximum methods will throw an IllegalArgumentException, alerting you that there’s no data to work with.

Complexity Analysis of Finding Min and Max Values in a BST

In the case of our findMinimum and findMaximum methods, the operations are quite straightforward. They each involve traversing the tree from the root to the leftmost or rightmost leaf node to find the min or max value, respectively.

Now, let’s break it down:

  • Time Complexity:

    • For findMinimum: We traverse from the root to the leftmost node. In the worst case, this means visiting each level of the tree once. Therefore, the time complexity is O(h), where h is the height of the tree.
    • For findMaximum: Similarly, we traverse to the rightmost node, which also results in a time complexity of O(h).
  • Space Complexity:

    • Both findMinimum and findMaximum methods use only a constant amount of extra space (to store the node references during the traversal), so the space complexity is O(1), which is excellent from a resource consumption standpoint.

It’s worth noting that the height of a BST can vary. In the best-case scenario, the tree is balanced, and the height h is log(n), where n is the number of nodes. In the worst case, the tree might be unbalanced, resembling a linked list, with a height of n. For a balanced tree, our methods would run in O(log(n)) time, which is very efficient.

Conclusion: Putting Theory into Practice

You now know how to navigate a Binary Search Tree to find the minimum and maximum values using Java. While the methods we discussed utilize iterative approaches, there’s always room for exploration and improvement. 🤔 Why not challenge yourself to reimplement these methods recursively?  It’s a great exercise to deepen your understanding of BSTs and recursive algorithms.

If you’re looking for more tree-based adventures, don’t forget to check out our other articles on BSTs and tree data structures. You can find them here.

💬 if you have any thoughts, questions, or insights, or if you’ve tried writing the recursive methods, please drop a comment below. Your input only helps us improve.

 Happy coding, and until next time 👨‍💻👩‍💻!

 

References

Leave a Reply

Discover more from Art of Coding

Subscribe now to keep reading and get access to the full archive.

Continue reading