Skip to content

Binary search tree implementation in java while learning data structures and algorithms

Notifications You must be signed in to change notification settings

Ian-Balijawa/binary-tree-implementation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 

Repository files navigation

binary search tree implementation in java

Binary Search Tree (BST)

A binary search tree is a special kind of [binary tree](../Binary%20Tree/) (a tree in which each node has at most two children) that performs insertions and deletions such that the tree is always sorted.

"Always sorted" property

Here is an example of a valid binary search tree:

A binary search tree

Notice how each left child is smaller than its parent node, and each right child is greater than its parent node. This is the key feature of a binary search tree.

For example, `2` is smaller than `7`, so it goes on the left; `5` is greater than `2`, so it goes on the right.

Inserting new nodes

When performing an insertion, we first compare the new value to the root node. If the new value is smaller, we take the *left* branch; if greater, we take the *right* branch. We work our way down the tree this way until we find an empty spot where we can insert the new value.

Suppose we want to insert the new value `9`:

- We start at the root of the tree (the node with the value `7`) and compare it to the new value `9`.
- `9 > 7`, so we go down the right branch and repeat the same procedure but this time on node `10`.
- Because `9 < 10`, we go down the left branch.
- We now arrived at a point where there are no more values to compare with. A new node for `9` is inserted at that location.

Here is the tree after inserting the new value `9`:

After adding 9

There is only one possible place where the new element can be inserted in the tree. Finding this place is usually quick. It takes **O(h)** time, where **h** is the height of the tree.

Note: The height of a node is the number of steps it takes to go from that node to its lowest leaf. The height of the entire tree is the distance from the root to the lowest leaf. Many of the operations on a binary search tree are expressed in terms of the tree's height.

By following this simple rule -- smaller values on the left, larger values on the right -- we keep the tree sorted, so whenever we query it, we can check if a value is in the tree.

Searching the tree

To find a value in the tree, we perform the same steps as with insertion:

- If the value is less than the current node, then take the left branch.
- If the value is greater than the current node, take the right branch.
- If the value is equal to the current node, we've found it!

Like most tree operations, this is performed recursively until either we find what we are looking for or run out of nodes to look at.

Here is an example for searching the value `5`:

Searching the tree

Searching is fast using the structure of the tree. It runs in O(h) time. If you have a well-balanced tree with a million nodes, it only takes about 20 steps to find anything in this tree. (The idea is very similar to binary search in an array.)

Traversing the tree

Sometimes you need to look at all nodes rather than only one.

There are three ways to traverse a binary tree:

  1. In-order (or depth-first): first look at the left child of a node then at the node itself and finally at its right child.
  2. Pre-order: first look at a node then its left and right children.
  3. Post-order: first look at the left and right children and process the node itself last.

Traversing the tree also happens recursively.

If you traverse a binary search tree in-order, it looks at all the nodes as if they were sorted from low to high. For the example tree, it would print 1, 2, 5, 7, 9, 10:

Traversing the tree

Deleting nodes

Removing nodes is easy. After removing a node, we replace the node with either its biggest child on the left or its smallest child on the right. That way the tree is still sorted after the removal. In the following example, 10 is removed and replaced with either 9 (Figure 2) or 11 (Figure 3).

Deleting a node with two children

Note the replacement needs to happen when the node has at least one child. If it has no child, you just disconnect it from its parent:

Deleting a leaf node

The code (solution 1)

So much for the theory. Let's see how we can implement a binary search tree in Java. There are different approaches you can take. First, I will show you how to make a class-based version, but we will also look at how to make one using enums.

Here is an example for a BinarySearchTree class:

public class Tree<E> {
  private class Node<T> {
    private E value;
    private Node<T> leftChild;
    private Node<T> rightChild;

    public Node(E value) {
      this.value = value;
    }

    @Override
    public String toString() {
      return "Node= " + value;
    }

    @Override
    public boolean equals(Object obj) {
      // nodes are equal if they have the same data or value

      return (int) this.value == (int) obj ? true : false;
    }
  }

Creating the root node

  private Node<E> root;
  ```

code for insering a node to the tree

    public void insert(E value) {
    var node = new Node<E>(value);

    if (root == null) {
      root = node;
      return;
    }

    var current = root;
    while (true) {
      if (((Comparable<E>) current.value).compareTo(value) > 0) {
        if (current.leftChild == null) {
          current.leftChild = node;
          break;
        }

        current = current.leftChild;
      } else {
        if (current.rightChild == null) {
          current.rightChild = node;
          break;
        }

        current = current.rightChild;
      }
    }
 }

```

# code to find a node a the tree
    ````Java

public boolean find(E value) {
    var current = root;
    while (current != null) {

      if (((Comparable<E>) value).compareTo(current.value) < 0)
        current = current.leftChild;
      else if (((Comparable<E>) value).compareTo(current.value) > 0)
        current = current.rightChild;
      else
        return true;
    }

    return false;
  }

Traversal Algorithms

    public void traversePreOrder() {
    traversePreOrder(root);
  }

  private void traversePreOrder(Node<E> root) {
    if (root == null)
      return;
    System.out.println(root.value);
    traversePreOrder(root.leftChild);
    traversePreOrder(root.rightChild);
  }

  public void traversePostOrder() {
    traversePostOrder(root);
  }

  private void traversePostOrder(Node<E> root) {
    if (root == null)
      return;
    traversePreOrder(root.leftChild);
    traversePreOrder(root.rightChild);
    System.out.println(root.value);
  }

  public void traverseLevelOrder() {
    traverseLevelOrder(root);
  }

  private void traverseLevelOrder(Node<E> root) {
    if (root == null)
      return;

    traverseLevelOrder(root.leftChild);
    System.out.println(root.value);
    traverseLevelOrder(root.rightChild);
  }

Inverting a binary tree

public void invertBinaryTree() {
    invertBinaryTree(root);
  }

  private void invertBinaryTree(Node<E> node) {
    if (node == null)
      return;
    Node<E> tmp = node.leftChild;
    node.leftChild = node.rightChild;
    node.rightChild = tmp;
    invertBinaryTree(node.leftChild);
    invertBinaryTree(node.rightChild);
  }

About

Binary search tree implementation in java while learning data structures and algorithms

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages