Files
hello-algo/ja/codes/ruby/chapter_tree/avl_tree.rb
Yudong Jin d7b2277d2b Re-translate the Japanese version (#1871)
* Retranslate Japanese docs with GPT-5.4

* Retranslate Japanese code with GPT-5.4
2026-03-30 07:30:15 +08:00

217 lines
6.0 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
=begin
File: avl_tree.rb
Created Time: 2024-04-17
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
=end
require_relative '../utils/tree_node'
require_relative '../utils/print_util'
### AVL 木 ###
class AVLTree
### コンストラクタ ###
def initialize
@root = nil
end
### 二分木の根ノードを取得 ###
def get_root
@root
end
### ノードの高さを取得 ###
def height(node)
# 空ノードの高さは -1、葉ードの高さは 0
return node.height unless node.nil?
-1
end
### ノードの高さを更新 ###
def update_height(node)
# ノードの高さは最も高い部分木の高さ + 1 に等しい
node.height = [height(node.left), height(node.right)].max + 1
end
### 平衡係数を取得 ###
def balance_factor(node)
# 空ノードの平衡係数は 0
return 0 if node.nil?
# ノードの平衡係数 = 左部分木の高さ - 右部分木の高さ
height(node.left) - height(node.right)
end
### 右回転操作 ###
def right_rotate(node)
child = node.left
grand_child = child.right
# child を支点として node を右回転させる
child.right = node
node.left = grand_child
# ノードの高さを更新する
update_height(node)
update_height(child)
# 回転後の部分木の根ノードを返す
child
end
### 左回転操作 ###
def left_rotate(node)
child = node.right
grand_child = child.left
# child を支点として node を左回転させる
child.left = node
node.right = grand_child
# ノードの高さを更新する
update_height(node)
update_height(child)
# 回転後の部分木の根ノードを返す
child
end
### 回転操作を行い、この部分木の平衡を回復する ###
def rotate(node)
# ノード node の平衡係数を取得
balance_factor = balance_factor(node)
# 左部分木をたどる
if balance_factor > 1
if balance_factor(node.left) >= 0
# 右回転
return right_rotate(node)
else
# 左回転してから右回転
node.left = left_rotate(node.left)
return right_rotate(node)
end
# 右に偏った木
elsif balance_factor < -1
if balance_factor(node.right) <= 0
# 左回転
return left_rotate(node)
else
# 右回転してから左回転
node.right = right_rotate(node.right)
return left_rotate(node)
end
end
# 平衡木なので回転不要、そのまま返す
node
end
### ノードを挿入 ###
def insert(val)
@root = insert_helper(@root, val)
end
# ## ノードを再帰的に挿入(補助メソッド)###
def insert_helper(node, val)
return TreeNode.new(val) if node.nil?
# 1. 挿入位置を探索してノードを挿入
if val < node.val
node.left = insert_helper(node.left, val)
elsif val > node.val
node.right = insert_helper(node.right, val)
else
# 重複ノードは挿入せず、そのまま返す
return node
end
# ノードの高さを更新する
update_height(node)
# 2. 回転操作を行い、部分木の平衡を回復する
rotate(node)
end
### ノードを削除 ###
def remove(val)
@root = remove_helper(@root, val)
end
# ## ノードを再帰的に削除(補助メソッド)###
def remove_helper(node, val)
return if node.nil?
# 1. ノードを探索して削除
if val < node.val
node.left = remove_helper(node.left, val)
elsif val > node.val
node.right = remove_helper(node.right, val)
else
if node.left.nil? || node.right.nil?
child = node.left || node.right
# 子ノード数 = 0 の場合、node をそのまま削除して返す
return if child.nil?
# 子ノード数 = 1 の場合、node をそのまま削除する
node = child
else
# 子ノード数 = 2 の場合、中順走査の次のノードを削除し、そのノードで現在のノードを置き換える
temp = node.right
while !temp.left.nil?
temp = temp.left
end
node.right = remove_helper(node.right, temp.val)
node.val = temp.val
end
end
# ノードの高さを更新する
update_height(node)
# 2. 回転操作を行い、部分木の平衡を回復する
rotate(node)
end
### ノードを検索 ###
def search(val)
cur = @root
# ループで探索し、葉ノードを越えたら抜ける
while !cur.nil?
# 目標ノードは cur の右部分木にある
if cur.val < val
cur = cur.right
# 目標ノードは cur の左部分木にある
elsif cur.val > val
cur = cur.left
# 目標ノードが見つかったらループを抜ける
else
break
end
end
# 目標ノードを返す
cur
end
end
### Driver Code ###
if __FILE__ == $0
def test_insert(tree, val)
tree.insert(val)
puts "\nノード #{val} を挿入した後、AVL 木は"
print_tree(tree.get_root)
end
def test_remove(tree, val)
tree.remove(val)
puts "\nノード #{val} を削除した後、AVL 木は"
print_tree(tree.get_root)
end
# 空の AVL 木を初期化する
avl_tree = AVLTree.new
# ノードを挿入する
# ノード挿入後に AVL 木がどのように平衡を保つかに注目
for val in [1, 2, 3, 4, 5, 8, 7, 9, 10, 6]
test_insert(avl_tree, val)
end
# 重複ノードを挿入する
test_insert(avl_tree, 7)
# ノードを削除する
# ノード削除後に AVL 木がどのように平衡を保つかに注目
test_remove(avl_tree, 8) # 次数 0 のノードを削除する
test_remove(avl_tree, 5) # 次数 1 のノードを削除する
test_remove(avl_tree, 4) # 次数 2 のノードを削除する
result_node = avl_tree.search(7)
puts "\n見つかったノードオブジェクトは #{result_node}、ノードの値 = #{result_node.val}"
end