BOM[12:00 AM]
PROFESSIONAL SOFTWARE ENGINEER
FOCUS ON WEB DESIGN AND DEVELOPMENT
● AVAILABLE FOR WORK
NYC[12:00 AM]
ARYAN KATHAWALE
SOFTWARE ENGINEER / DESIGNER
HELPING WITH FULLSTACK , SYSTEM DESIGN ,UI/UX , LOW LEVEL
4+ YEARS OF EXPERIENCE AS A GENERALIST DEVELOPER
BOM[12:00 AM]
MENU
A
R

EXPERIENTAL
DESIGN ENGINEER

Y

DEVELOPER

A

UI/UX

N
K
ARYAN KATHAWALE
SOFTWARE ENGINEER / DESIGNER
HELPING WITH : SYSTEM DESIGN ,
UI/UX , LOW LEVEL 4+ YEARS OF EXPERIENCE AS A GENERALIST DEVELOPER

This blog is about trees

2025-3-22

This blog is about trees
"The best time to plant a tree was 20 years ago. The second best time is now."

Understanding Binary Trees in Computer Science

Binary trees are fundamental data structures in computer science that form the backbone of many algorithms and applications. Unlike their biological counterparts, these digital trees grow downward, with their roots at the top and branches extending below.

What Is a Binary Tree?

A binary tree is a hierarchical data structure where each node has at most two children, referred to as the left child and the right child. This simple constraint creates a powerful organizational framework that enables efficient data storage, retrieval, and manipulation.

The structure begins with a root node at the top and branches out below. Nodes with no children are called leaf nodes, marking the endpoints of the tree's paths.

Basic Implementation in Python

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

With this simple class, we can create a binary tree structure:

# Create a simple binary tree
#      10
#     /  \
#    5    15
#   / \   / \
#  3   7 12  18

root = TreeNode(10)
root.left = TreeNode(5)
root.right = TreeNode(15)
root.left.left = TreeNode(3)
root.left.right = TreeNode(7)
root.right.left = TreeNode(12)
root.right.right = TreeNode(18)

Key Binary Tree Types

Binary Search Trees (BST)

Binary search trees maintain a specific ordering property: for any node, all elements in its left subtree have values less than the node's value, and all elements in its right subtree have values greater than the node's value. This ordering enables efficient searching, insertion, and deletion operations, typically with O(log n) time complexity in balanced trees.

    50
   /  \
  30   70
 / \   / \
20 40 60 80

BST Implementation in JavaScript

class BSTNode {
  constructor(value) {
    this.value = value;
    this.left = null;
    this.right = null;
  }
}

class BinarySearchTree {
  constructor() {
    this.root = null;
  }

  insert(value) {
    const newNode = new BSTNode(value);
    
    if (!this.root) {
      this.root = newNode;
      return this;
    }
    
    let current = this.root;
    
    while (true) {
      if (value === current.value) return undefined;
      if (value < current.value) {
        if (!current.left) {
          current.left = newNode;
          return this;
        }
        current = current.left;
      } else {
        if (!current.right) {
          current.right = newNode;
          return this;
        }
        current = current.right;
      }
    }
  }
  
  search(value) {
    if (!this.root) return false;
    
    let current = this.root;
    while (current) {
      if (value === current.value) return true;
      current = value < current.value ? current.left : current.right;
    }
    return false;
  }
}

// Usage
const bst = new BinarySearchTree();
bst.insert(50);
bst.insert(30);
bst.insert(70);
bst.insert(20);
bst.insert(40);
bst.insert(60);
bst.insert(80);

console.log(bst.search(40)); // true
console.log(bst.search(90)); // false

AVL Trees

Named after their inventors (Adelson-Velsky and Landis), AVL trees are self-balancing binary search trees. They maintain balance by ensuring the height difference between left and right subtrees of any node never exceeds one. When this balance is violated after an insertion or deletion, the tree performs rotations to restore balance.

AVL Tree Rotations in Java

class AVLNode {
    int value;
    int height;
    AVLNode left, right;
    
    AVLNode(int value) {
        this.value = value;
        this.height = 1;
    }
}

class AVLTree {
    AVLNode root;
    
    // Get height of node
    int height(AVLNode node) {
        if (node == null) return 0;
        return node.height;
    }
    
    // Get balance factor
    int getBalance(AVLNode node) {
        if (node == null) return 0;
        return height(node.left) - height(node.right);
    }
    
    // Right rotation
    AVLNode rightRotate(AVLNode y) {
        AVLNode x = y.left;
        AVLNode T2 = x.right;
        
        // Perform rotation
        x.right = y;
        y.left = T2;
        
        // Update heights
        y.height = Math.max(height(y.left), height(y.right)) + 1;
        x.height = Math.max(height(x.left), height(x.right)) + 1;
        
        // Return new root
        return x;
    }
    
    // Left rotation
    AVLNode leftRotate(AVLNode x) {
        AVLNode y = x.right;
        AVLNode T2 = y.left;
        
        // Perform rotation
        y.left = x;
        x.right = T2;
        
        // Update heights
        x.height = Math.max(height(x.left), height(x.right)) + 1;
        y.height = Math.max(height(y.left), height(y.right)) + 1;
        
        // Return new root
        return y;
    }
}

Red-Black Trees

Red-black trees use color properties and specific rules to maintain balance. Each node is colored either red or black, with constraints ensuring no path from the root to a leaf is more than twice as long as any other path. These trees provide efficient worst-case guarantees for search, insert, and delete operations.

Complete Binary Trees

In a complete binary tree, all levels are fully filled except possibly the last level, which is filled from left to right. This property makes complete binary trees ideal for array implementations, as used in binary heaps.

Binary Heap Implementation in Python

class MinHeap:
    def __init__(self):
        self.heap = []
    
    def parent(self, i):
        return (i - 1) // 2
    
    def left_child(self, i):
        return 2 * i + 1
    
    def right_child(self, i):
        return 2 * i + 2
    
    def insert(self, value):
        self.heap.append(value)
        self._sift_up(len(self.heap) - 1)
    
    def extract_min(self):
        if not self.heap:
            return None
        
        min_value = self.heap[0]
        last_value = self.heap.pop()
        
        if self.heap:
            self.heap[0] = last_value
            self._sift_down(0)
        
        return min_value
    
    def _sift_up(self, i):
        parent = self.parent(i)
        if i > 0 and self.heap[parent] > self.heap[i]:
            self.heap[parent], self.heap[i] = self.heap[i], self.heap[parent]
            self._sift_up(parent)
    
    def _sift_down(self, i):
        min_index = i
        left = self.left_child(i)
        right = self.right_child(i)
        
        if left < len(self.heap) and self.heap[left] < self.heap[min_index]:
            min_index = left
        
        if right < len(self.heap) and self.heap[right] < self.heap[min_index]:
            min_index = right
        
        if i != min_index:
            self.heap[i], self.heap[min_index] = self.heap[min_index], self.heap[i]
            self._sift_down(min_index)

Full Binary Trees

A full binary tree (sometimes called a proper binary tree) is one in which every node has either 0 or 2 children—no node has exactly one child.

Tree Traversals

Trees can be traversed in several ways, each providing a different order of node visits:

def in_order_traversal(node):
    if node:
        in_order_traversal(node.left)
        print(node.value, end=" ")
        in_order_traversal(node.right)

def pre_order_traversal(node):
    if node:
        print(node.value, end=" ")
        pre_order_traversal(node.left)
        pre_order_traversal(node.right)

def post_order_traversal(node):
    if node:
        post_order_traversal(node.left)
        post_order_traversal(node.right)
        print(node.value, end=" ")

def level_order_traversal(root):
    if not root:
        return
    
    queue = [root]
    while queue:
        node = queue.pop(0)
        print(node.value, end=" ")
        
        if node.left:
            queue.append(node.left)
        if node.right:
            queue.append(node.right)

Applications of Binary Trees

Binary trees power numerous computer applications:

  • Database indexing: B-trees and their variants optimize data access in databases
  • Expression evaluation: Binary expression trees represent and evaluate arithmetic expressions
  • Huffman coding: Used for data compression
  • Priority queues: Implemented efficiently using binary heaps
  • Routing algorithms: Specialized binary trees help route network traffic

Binary Trees in the Wild

The concept of binary trees extends beyond computer science. Decision trees in machine learning, game trees in artificial intelligence, and even organizational hierarchies all utilize tree-like structures to model relationships and decision paths.

Problem Solving with Binary Trees

Let's solve a classic problem: determining if a binary tree is balanced.

def is_balanced(root):
    """
    Check if a binary tree is height-balanced:
    A height-balanced tree is defined as a tree in which 
    the height of the left and right subtrees of any node 
    differ by no more than 1.
    """
    def check_height(node):
        if not node:
            return 0
            
        left_height = check_height(node.left)
        if left_height == -1:
            return -1
            
        right_height = check_height(node.right)
        if right_height == -1:
            return -1
            
        if abs(left_height - right_height) > 1:
            return -1
            
        return max(left_height, right_height) + 1
    
    return check_height(root) != -1

comment on bluesky / mastodon / twitter / rss

HEY THERE,
[GENERAL]
[Platforms]
[Personal Details]
(+91) 8421-911-353
RESUME
[ADDRESS]
CBD Belapur
Mumbai , Maharashtra , India.
Folio` 2024 © Aryan Kathawale