Files
Yudong Jin 2f561d3d51 feat(ko): add the Korean online edition (#2006)
* feat(ko): add the complete Korean online edition

* Use UTF-8 for Python CI output on Windows

* Remove print-book promotion from the Korean site

* fix(ko): preserve emphasis before Korean particles
2026-10-29 03:46:43 +08:00

217 lines
6.0 KiB
Ruby

=begin
File: avl_tree.rb
Created Time: 2024-04-17
Author: Xuan Khoa Tu Nguyen ([email protected])
=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
### 실행 코드 ###
if __FILE__ == $0
def test_insert(tree, val)
tree.insert(val)
puts "\n노드 삽입 후의 AVL 트리(노드: #{val})"
print_tree(tree.get_root)
end
def test_remove(tree, val)
tree.remove(val)
puts "\n노드 삭제 후의 AVL 트리(노드: #{val})"
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