mirror of
https://github.com/TheAlgorithms/Ruby
synced 2024-12-25 21:58:57 +01:00
281 lines
7.3 KiB
Ruby
281 lines
7.3 KiB
Ruby
class AvlTreeNode
|
|
|
|
attr_reader :key
|
|
attr_accessor :parent
|
|
attr_accessor :left
|
|
attr_accessor :right
|
|
attr_accessor :height
|
|
|
|
def initialize(key, parent=nil)
|
|
@key = key
|
|
@parent = parent
|
|
@height = 1
|
|
end
|
|
end
|
|
|
|
##
|
|
# This class represents an AVL tree (a self-balancing binary search tree) with distinct node keys.
|
|
# Starting from the root, every node has up to two children (one left and one right child node).
|
|
#
|
|
# For the BST property:
|
|
# - the keys of nodes in the left subtree of a node are strictly less than the key of the node;
|
|
# - the keys of nodes in the right subtree of a node are strictly greater than the key of the node.
|
|
#
|
|
# Due to self-balancing upon key insertion and deletion, the main operations of this data structure
|
|
# (insertion, deletion, membership) run - in worst case - in O(log(n)), where n is the number of nodes in the tree.
|
|
|
|
class AvlTree
|
|
|
|
attr_reader :size
|
|
attr_accessor :root
|
|
|
|
def initialize(keys=[])
|
|
@size = 0
|
|
keys.each {|key| insert_key(key) }
|
|
end
|
|
|
|
def empty?
|
|
size == 0
|
|
end
|
|
|
|
def insert_key(key)
|
|
@size += 1
|
|
if root.nil?
|
|
@root = AvlTreeNode.new(key)
|
|
return
|
|
end
|
|
parent = root
|
|
while (key < parent.key && !parent.left.nil? && parent.left.key != key) ||
|
|
(key > parent.key && !parent.right.nil? && parent.right.key != key)
|
|
parent = key < parent.key ? parent.left : parent.right
|
|
end
|
|
if key < parent.key
|
|
raise ArgumentError.new("Key #{key} is already present in the AvlTree") unless parent.left.nil?
|
|
parent.left = AvlTreeNode.new(key, parent)
|
|
else
|
|
raise ArgumentError.new("Key #{key} is already present in the AvlTree") unless parent.right.nil?
|
|
parent.right = AvlTreeNode.new(key, parent)
|
|
end
|
|
balance(parent)
|
|
end
|
|
|
|
def min_key(node=root)
|
|
return nil if node.nil?
|
|
min_key_node(node).key
|
|
end
|
|
|
|
def max_key(node=root)
|
|
return nil if node.nil?
|
|
max_key_node(node).key
|
|
end
|
|
|
|
def contains_key?(key)
|
|
!find_node_with_key(key).nil?
|
|
end
|
|
|
|
def delete_key(key)
|
|
parent = find_parent_of_node_with_key(key)
|
|
if parent.nil?
|
|
return if root.nil? || root.key != key
|
|
@size -= 1
|
|
@root = adjusted_subtree_after_deletion(root.left, root.right)
|
|
root.parent = nil
|
|
balance(root.right.nil? ? root : root.right)
|
|
return
|
|
end
|
|
if key < parent.key
|
|
node = parent.left
|
|
parent.left = adjusted_subtree_after_deletion(node.left, node.right)
|
|
unless parent.left.nil?
|
|
parent.left.parent = parent
|
|
balance(parent.left.right.nil? ? parent.left : parent.left.right)
|
|
end
|
|
else
|
|
node = parent.right
|
|
parent.right = adjusted_subtree_after_deletion(node.left, node.right)
|
|
unless parent.right.nil?
|
|
parent.right.parent = parent
|
|
balance(parent.right.right.nil? ? parent.right : parent.right.right)
|
|
end
|
|
end
|
|
@size -= 1
|
|
end
|
|
|
|
def traverse_preorder(key_consumer, node=root)
|
|
return if node.nil?
|
|
key_consumer.call(node.key)
|
|
traverse_preorder(key_consumer, node.left) unless node.left.nil?
|
|
traverse_preorder(key_consumer, node.right) unless node.right.nil?
|
|
end
|
|
|
|
def traverse_inorder(key_consumer, node=root)
|
|
return if node.nil?
|
|
traverse_inorder(key_consumer, node.left) unless node.left.nil?
|
|
key_consumer.call(node.key)
|
|
traverse_inorder(key_consumer, node.right) unless node.right.nil?
|
|
end
|
|
|
|
def traverse_postorder(key_consumer, node=root)
|
|
return if node.nil?
|
|
traverse_postorder(key_consumer, node.left) unless node.left.nil?
|
|
traverse_postorder(key_consumer, node.right) unless node.right.nil?
|
|
key_consumer.call(node.key)
|
|
end
|
|
|
|
def to_array(visit_traversal=:traverse_preorder)
|
|
visited = []
|
|
method(visit_traversal).call(->(key) { visited.append(key) })
|
|
visited
|
|
end
|
|
|
|
private
|
|
def min_key_node(node=root)
|
|
return nil if node.nil?
|
|
until node.left.nil?
|
|
node = node.left
|
|
end
|
|
node
|
|
end
|
|
|
|
def max_key_node(node=root)
|
|
return nil if node.nil?
|
|
until node.right.nil?
|
|
node = node.right
|
|
end
|
|
node
|
|
end
|
|
|
|
def find_node_with_key(key)
|
|
node = root
|
|
until node.nil? || node.key == key
|
|
node = key < node.key ? node.left : node.right
|
|
end
|
|
node
|
|
end
|
|
|
|
def find_parent_of_node_with_key(key)
|
|
return nil if root.nil? || root.key == key
|
|
parent = root
|
|
until parent.nil?
|
|
if key < parent.key
|
|
return nil if parent.left.nil?
|
|
return parent if parent.left.key == key
|
|
parent = parent.left
|
|
else
|
|
return nil if parent.right.nil?
|
|
return parent if parent.right.key == key
|
|
parent = parent.right
|
|
end
|
|
end
|
|
nil
|
|
end
|
|
|
|
def adjusted_subtree_after_deletion(left, right)
|
|
return right if left.nil?
|
|
return left if right.nil?
|
|
if right.left.nil?
|
|
right.left = left
|
|
left.parent = right
|
|
return right
|
|
end
|
|
successor_parent = right
|
|
until successor_parent.left.left.nil?
|
|
successor_parent = successor_parent.left
|
|
end
|
|
successor = successor_parent.left
|
|
successor_parent.left = successor.right
|
|
successor.right.parent = successor_parent unless successor.right.nil?
|
|
successor.right = right
|
|
right.parent = successor
|
|
successor.left = left
|
|
left.parent = successor
|
|
successor
|
|
end
|
|
|
|
def balance(node)
|
|
return if node.nil?
|
|
left_height = node.left&.height || 0
|
|
right_height = node.right&.height || 0
|
|
# Assumption: the subtrees rooted at `node.left` and `node.right` are balanced
|
|
adjust_height(node)
|
|
if right_height - left_height > 1
|
|
# `node` is right-heavy
|
|
if !node.right.left.nil? && (node.right.left.height || 0) > (node.right.right&.height || 0)
|
|
rotate_right_left(node)
|
|
else
|
|
rotate_left(node)
|
|
end
|
|
elsif left_height - right_height > 1
|
|
# `node` is left-heavy
|
|
if !node.left.right.nil? && (node.left.right.height || 0) > (node.left.left&.height || 0)
|
|
rotate_left_right(node)
|
|
else
|
|
rotate_right(node)
|
|
end
|
|
end
|
|
|
|
balance(node.parent)
|
|
end
|
|
|
|
def rotate_left(node)
|
|
new_root = node.right
|
|
if node == root
|
|
@root = new_root
|
|
elsif node.parent.left == node
|
|
node.parent.left = new_root
|
|
else
|
|
node.parent.right = new_root
|
|
end
|
|
new_root.parent = node.parent
|
|
if new_root.left.nil?
|
|
node.right = nil
|
|
new_root.left = node
|
|
node.parent = new_root
|
|
else
|
|
node.right = new_root.left
|
|
new_root.left.parent = node
|
|
new_root.left = node
|
|
node.parent = new_root
|
|
end
|
|
adjust_height(node)
|
|
adjust_height(new_root)
|
|
end
|
|
|
|
def rotate_right(node)
|
|
new_root = node.left
|
|
if node == root
|
|
@root = new_root
|
|
elsif node.parent.left == node
|
|
node.parent.left = new_root
|
|
else
|
|
node.parent.right = new_root
|
|
end
|
|
new_root.parent = node.parent
|
|
if new_root.right.nil?
|
|
node.left = nil
|
|
new_root.right = node
|
|
node.parent = new_root
|
|
else
|
|
node.left = new_root.right
|
|
new_root.right.parent = node
|
|
new_root.right = node
|
|
node.parent = new_root
|
|
end
|
|
adjust_height(node)
|
|
adjust_height(new_root)
|
|
end
|
|
|
|
def rotate_right_left(node)
|
|
rotate_right(node.right)
|
|
rotate_left(node)
|
|
end
|
|
|
|
def rotate_left_right(node)
|
|
rotate_left(node.left)
|
|
rotate_right(node)
|
|
end
|
|
|
|
def adjust_height(node)
|
|
node.height = 1 + [node.left&.height || 0, node.right&.height || 0].max
|
|
end
|
|
end
|