241 lines
7.2 KiB
Ada
241 lines
7.2 KiB
Ada
with Ada.Text_IO, Ada.Finalization, Ada.Unchecked_Deallocation;
|
|
|
|
procedure Main is
|
|
|
|
generic
|
|
type Key_Type is private;
|
|
with function "<"(a, b : Key_Type) return Boolean is <>;
|
|
with function "="(a, b : Key_Type) return Boolean is <>;
|
|
with function "<="(a, b : Key_Type) return Boolean is <>;
|
|
package AVL_Tree is
|
|
type Tree is tagged limited private;
|
|
function insert(self : in out Tree; key : Key_Type) return Boolean;
|
|
procedure delete(self : in out Tree; key : Key_Type);
|
|
procedure print_balance(self : in out Tree);
|
|
|
|
private
|
|
type Height_Amt is range -1 .. Integer'Last;
|
|
|
|
-- Since only one key is inserted before each rebalance, the balance of
|
|
-- all trees/subtrees will stay in range -2 .. 2
|
|
type Balance_Amt is range -2 .. 2;
|
|
|
|
type Node;
|
|
type Node_Ptr is access Node;
|
|
type Node is new Ada.Finalization.Limited_Controlled with record
|
|
left, right, parent : Node_Ptr := null;
|
|
key : Key_Type;
|
|
balance : Balance_Amt := 0;
|
|
end record;
|
|
overriding procedure Finalize(self : in out Node);
|
|
subtype Node_Parent is Ada.Finalization.Limited_Controlled;
|
|
|
|
type Tree is new Ada.Finalization.Limited_Controlled with record
|
|
root : Node_Ptr := null;
|
|
end record;
|
|
overriding procedure Finalize(self : in out Tree);
|
|
|
|
end AVL_Tree;
|
|
|
|
package body AVL_Tree is
|
|
|
|
procedure Free_Node is new Ada.Unchecked_Deallocation(Node, Node_Ptr);
|
|
|
|
overriding procedure Finalize(self : in out Node) is
|
|
begin
|
|
Free_Node(self.left);
|
|
Free_Node(self.right);
|
|
end Finalize;
|
|
|
|
overriding procedure Finalize(self : in out Tree) is
|
|
begin
|
|
Free_Node(self.root);
|
|
end Finalize;
|
|
|
|
|
|
function height(n : Node_Ptr) return Height_Amt is
|
|
begin
|
|
if n = null then
|
|
return -1;
|
|
else
|
|
return 1 + Height_Amt'Max(height(n.left), height(n.right));
|
|
end if;
|
|
end height;
|
|
|
|
procedure set_balance(n : not null Node_Ptr) is
|
|
begin
|
|
n.balance := Balance_Amt(height(n.right) - height(n.left));
|
|
end set_balance;
|
|
|
|
procedure update_parent(parent : Node_Ptr; new_child : Node_Ptr; old_child : Node_Ptr) is
|
|
begin
|
|
if parent /= null then
|
|
if parent.right = old_child then
|
|
parent.right := new_child;
|
|
else
|
|
parent.left := new_child;
|
|
end if;
|
|
end if;
|
|
end update_parent;
|
|
|
|
function rotate_left(a : not null Node_Ptr) return Node_Ptr is
|
|
b : Node_Ptr := a.right;
|
|
begin
|
|
b.parent := a.parent;
|
|
a.right := b.left;
|
|
if a.right /= null then
|
|
a.right.parent := a;
|
|
end if;
|
|
b.left := a;
|
|
a.parent := b;
|
|
update_parent(parent => b.parent, new_child => b, old_child => a);
|
|
|
|
set_balance(a);
|
|
set_balance(b);
|
|
return b;
|
|
end rotate_left;
|
|
|
|
function rotate_right(a : not null Node_Ptr) return Node_Ptr is
|
|
b : Node_Ptr := a.left;
|
|
begin
|
|
b.parent := a.parent;
|
|
a.left := b.right;
|
|
if a.left /= null then
|
|
a.left.parent := a;
|
|
end if;
|
|
b.right := a;
|
|
a.parent := b;
|
|
update_parent(parent => b.parent, new_child => b, old_child => a);
|
|
|
|
set_balance(a);
|
|
set_balance(b);
|
|
return b;
|
|
end rotate_right;
|
|
|
|
function rotate_left_right(n : not null Node_Ptr) return Node_Ptr is
|
|
begin
|
|
n.left := rotate_left(n.left);
|
|
return rotate_right(n);
|
|
end rotate_left_right;
|
|
|
|
function rotate_right_left(n : not null Node_Ptr) return Node_Ptr is
|
|
begin
|
|
n.right := rotate_right(n.right);
|
|
return rotate_left(n);
|
|
end rotate_right_left;
|
|
|
|
procedure rebalance(self : in out Tree; n : not null Node_Ptr) is
|
|
new_n : Node_Ptr := n;
|
|
begin
|
|
set_balance(new_n);
|
|
if new_n.balance = -2 then
|
|
if height(new_n.left.left) >= height(new_n.left.right) then
|
|
new_n := rotate_right(new_n);
|
|
else
|
|
new_n := rotate_left_right(new_n);
|
|
end if;
|
|
elsif new_n.balance = 2 then
|
|
if height(new_n.right.right) >= height(new_n.right.left) then
|
|
new_n := rotate_left(new_n);
|
|
else
|
|
new_n := rotate_right_left(new_n);
|
|
end if;
|
|
end if;
|
|
|
|
if new_n.parent /= null then
|
|
rebalance(self, new_n.parent);
|
|
else
|
|
self.root := new_n;
|
|
end if;
|
|
end rebalance;
|
|
|
|
function new_node(key : Key_Type) return Node_Ptr is
|
|
(new Node'(Node_Parent with key => key, others => <>));
|
|
|
|
function insert(self : in out Tree; key : Key_Type) return Boolean is
|
|
curr, parent : Node_Ptr;
|
|
go_left : Boolean;
|
|
begin
|
|
if self.root = null then
|
|
self.root := new_node(key);
|
|
return True;
|
|
end if;
|
|
|
|
curr := self.root;
|
|
while curr.key /= key loop
|
|
parent := curr;
|
|
go_left := key < curr.key;
|
|
curr := (if go_left then curr.left else curr.right);
|
|
if curr = null then
|
|
if go_left then
|
|
parent.left := new_node(key);
|
|
parent.left.parent := parent;
|
|
else
|
|
parent.right := new_node(key);
|
|
parent.right.parent := parent;
|
|
end if;
|
|
rebalance(self, parent);
|
|
return True;
|
|
end if;
|
|
end loop;
|
|
return False;
|
|
end insert;
|
|
|
|
procedure delete(self : in out Tree; key : Key_Type) is
|
|
successor, parent, child : Node_Ptr := self.root;
|
|
to_delete : Node_Ptr := null;
|
|
begin
|
|
if self.root = null then
|
|
return;
|
|
end if;
|
|
|
|
while child /= null loop
|
|
parent := successor;
|
|
successor := child;
|
|
child := (if successor.key <= key then successor.right else successor.left);
|
|
if successor.key = key then
|
|
to_delete := successor;
|
|
end if;
|
|
end loop;
|
|
|
|
if to_delete = null then
|
|
return;
|
|
end if;
|
|
to_delete.key := successor.key;
|
|
child := (if successor.left = null then successor.right else successor.left);
|
|
if self.root.key = key then
|
|
self.root := child;
|
|
else
|
|
update_parent(parent => parent, new_child => child, old_child => successor);
|
|
rebalance(self, parent);
|
|
end if;
|
|
Free_Node(successor);
|
|
end delete;
|
|
|
|
procedure print_balance(n : Node_Ptr) is
|
|
begin
|
|
if n /= null then
|
|
print_balance(n.left);
|
|
Ada.Text_IO.Put(n.balance'Image);
|
|
print_balance(n.right);
|
|
end if;
|
|
end print_balance;
|
|
|
|
procedure print_balance(self : in out Tree) is
|
|
begin
|
|
print_balance(self.root);
|
|
end print_balance;
|
|
end AVL_Tree;
|
|
|
|
package Int_AVL_Tree is new AVL_Tree(Integer);
|
|
|
|
tree : Int_AVL_Tree.Tree;
|
|
success : Boolean;
|
|
begin
|
|
for i in 1 .. 10 loop
|
|
success := tree.insert(i);
|
|
end loop;
|
|
Ada.Text_IO.Put("Printing balance: ");
|
|
tree.print_balance;
|
|
Ada.Text_IO.New_Line;
|
|
end Main;
|