AVL Trees (Adelson-Velsky and Landis Trees)

AVL trees are self-balancing binary search trees designed to maintain balance during insertions and deletions. In an AVL tree, the heights of the two child subtrees of any node differ by at most one. This ensures that the tree remains balanced, which helps in maintaining efficient search, insertion, and deletion operations.

Balancing Operations

AVL trees employ rotation operations to maintain balance. There are four types of rotations:

  1. Left rotation
  2. Right rotation
  3. Left-right rotation (also known as double rotation)
  4. Right-left rotation (also known as double rotation)

Insertion

When inserting a new node into an AVL tree, the tree may become unbalanced. To restore balance, rotation operations are performed as necessary.

Deletion

Similarly, when deleting a node from an AVL tree, the tree may become unbalanced. Rotation operations are employed to restore balance in this scenario as well.

Below is the Ruby program to implement AVL Tree:

Ruby
class AVLNode
  attr_accessor :value, :left, :right, :height

  def initialize(value)
    @value = value
    @left = nil
    @right = nil
    @height = 1
  end
end

class AVLTree
  attr_reader :root

  def initialize
    @root = nil
  end

  def insert(value)
    @root = insert_node(@root, value)
  end

  def inorder_traversal(node = @root)
    return unless node

    inorder_traversal(node.left)
    puts node.value
    inorder_traversal(node.right)
  end

  private

  def insert_node(node, value)
    return AVLNode.new(value) unless node

    if value < node.value
      node.left = insert_node(node.left, value)
    else
      node.right = insert_node(node.right, value)
    end

    node.height = [height(node.left), height(node.right)].max + 1

    balance = balance_factor(node)

    # Perform rotations if the node becomes unbalanced
    if balance > 1 && value < node.left.value
      return right_rotate(node)
    end

    if balance < -1 && value > node.right.value
      return left_rotate(node)
    end

    if balance > 1 && value > node.left.value
      node.left = left_rotate(node.left)
      return right_rotate(node)
    end

    if balance < -1 && value < node.right.value
      node.right = right_rotate(node.right)
      return left_rotate(node)
    end

    node
  end

  def height(node)
    return 0 unless node

    node.height
  end

  def balance_factor(node)
    return 0 unless node

    height(node.left) - height(node.right)
  end

  def right_rotate(y)
    x = y.left
    t2 = x.right

    x.right = y
    y.left = t2

    y.height = [height(y.left), height(y.right)].max + 1
    x.height = [height(x.left), height(x.right)].max + 1

    x
  end

  def left_rotate(x)
    y = x.right
    t2 = y.left

    y.left = x
    x.right = t2

    x.height = [height(x.left), height(x.right)].max + 1
    y.height = [height(y.left), height(y.right)].max + 1

    y
  end
end

avl_tree = AVLTree.new
avl_tree.insert(10)
avl_tree.insert(20)
avl_tree.insert(30)
avl_tree.insert(40)
avl_tree.insert(50)
avl_tree.insert(25)

puts "In-order traversal of AVL tree:"
avl_tree.inorder_traversal

Output:

How to Implement Data Structures in Ruby?

Data structures are fundamental components of any programming language, allowing developers to organize and manipulate data efficiently. In Ruby, a versatile and expressive language, implementing various data structures is straightforward. In this article, we’ll explore how to implement common data structures such as arrays, linked lists, stacks, queues, trees, graphs, and hashmaps in Ruby. The article focuses on discussing data structures in Ruby.

Table of Content

  • Singly-Linked Lists
  • Doubly-Linked Lists
  • Circular Linked Lists
  • Queues
  • Stack
  • Hash Tables
  • Sets
  • Binary Trees
  • AVL Trees (Adelson-Velsky and Landis Trees)
  • Graphs
  • Persistent Lists

Similar Reads

Singly-Linked Lists

A singly linked list is a linear data structure consisting of a sequence of elements, where each element points to the next element in the sequence. The last element points to nil, indicating the end of the list. Singly-linked lists are simple to implement and efficient for operations such as insertion and deletion at the beginning of the list....

Doubly-Linked Lists

A doubly-linked list is a type of linked list where each element contains pointers to both the next and previous elements in the sequence. This allows for traversal in both forward and backward directions. Doubly-linked lists support efficient insertion and deletion operations at both ends of the list....

Circular Linked Lists

A circular linked list is a variation of a linked list where the last element points back to the first element, forming a circular structure. This can be achieved by making the next pointer of the last node point to the first node. Circular linked lists can be singly or doubly-linked and are useful for applications like managing circular buffers or implementing algorithms like Floyd’s cycle detection algorithm....

Queues

A queue is a linear data structure that follows the First-In-First-Out (FIFO) principle. In a queue, elements are inserted at the rear (enqueue) and removed from the front (dequeue). It operates in a similar way to a queue of people waiting in line for a service....

Stack

A stack is a linear data structure that follows the Last-In-First-Out (LIFO) principle. In a stack, elements are inserted and removed from the same end, called the top of the stack. It operates similarly to a stack of plates where you can only add or remove the top plate....

Hash Tables

A hash table is a data structure that stores key-value pairs. It uses a hash function to compute an index into an array where the desired value can be found. Hash tables offer efficient lookup, insertion, and deletion operations....

Sets

In Ruby, a set is a collection of unique elements, meaning each element appears only once in the set. Sets are useful for tasks where you need to ensure uniqueness or perform set operations like union, intersection, and difference....

Binary Trees

A binary tree is a hierarchical data structure in which each node has at most two children, referred to as the left child and the right child. It consists of nodes, where each node contains a value and references to its left and right children....

AVL Trees (Adelson-Velsky and Landis Trees)

AVL trees are self-balancing binary search trees designed to maintain balance during insertions and deletions. In an AVL tree, the heights of the two child subtrees of any node differ by at most one. This ensures that the tree remains balanced, which helps in maintaining efficient search, insertion, and deletion operations....

Graphs

A graph is a data structure consisting of a set of vertices (nodes) and a set of edges connecting these vertices. Graphs are widely used to represent relationships between objects, such as social networks, computer networks, and transportation networks. They can be directed (edges have a specific direction) or undirected (edges have no direction)....

Persistent Lists

Persistent lists are immutable data structures where elements cannot be modified after creation. Operations on persistent lists create new instances with the desired changes while preserving the original list. This ensures that the original list remains unchanged, making persistent lists ideal for functional programming....