Skip to content

Commit 79cabfc

Browse files
authored
Merge pull request #640 from eulerkochy/avl_tree
AVL Tree
2 parents 21bc723 + 541cd12 commit 79cabfc

File tree

8 files changed

+460
-3
lines changed

8 files changed

+460
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ This package implements a variety of data structures, including
3131
- SparseIntSet
3232
- DiBitVector (in which each element can store two bits)
3333
- Red Black Tree
34+
- AVL Tree
3435

3536
Resources
3637
---------

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ makedocs(
2626
"sorted_containers.md",
2727
"dibit_vector.md",
2828
"red_black_tree.md",
29+
"avl_tree.md",
2930
],
3031
modules = [DataStructures],
3132
format = Documenter.HTML()

docs/src/avl_tree.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
```@meta
2+
DocTestSetup = :(using DataStructures)
3+
```
4+
5+
# AVL Tree
6+
7+
The `AVLTree` type is an implementation of AVL Tree in Julia. It is a self-balancing binary search tree where balancing occurs based on the difference of height of the left subtree and the right subtree. Operations such as search, insert and delete can be done in `O(log n)` complexity, where `n` is the number of nodes in the `AVLTree`. Order-statistics on the keys can also be done in `O(log n)`.
8+
9+
Examples:
10+
11+
```jldoctest
12+
julia> tree = AVLTree{Int}();
13+
14+
julia> for k in 1:2:20
15+
push!(tree, k)
16+
end
17+
18+
julia> haskey(tree, 3)
19+
true
20+
21+
julia> tree[4] # time complexity of this operation is O(log n)
22+
7
23+
24+
julia> for k in 1:2:10
25+
delete!(tree, k)
26+
end
27+
28+
julia> haskey(tree, 5)
29+
false
30+
31+
julia> sorted_rank(tree, 17) # used for finding rank of the key
32+
4
33+
```
34+
35+
```@meta
36+
DocTestSetup = nothing
37+
```

docs/src/index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This package implements a variety of data structures, including
2323
- SparseIntSet
2424
- DiBitVector
2525
- Red Black Tree
26+
- AVL Tree
2627

2728
## Contents
2829

@@ -48,6 +49,7 @@ Pages = [
4849
"sorted_containers.md",
4950
"sparse_int_set.md",
5051
"dibit_vector.md",
51-
"red_black_tree.md"
52+
"red_black_tree.md",
53+
"avl_tree.md"
5254
]
5355
```

src/DataStructures.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ module DataStructures
6060
export DiBitVector
6161

6262
export RBTree, search_node, minimum_node
63+
export AVLTree, sorted_rank
6364

6465
export findkey
6566

@@ -112,7 +113,8 @@ module DataStructures
112113
export SparseIntSet
113114

114115
include("dibit_vector.jl")
116+
include("avl_tree.jl")
115117
include("red_black_tree.jl")
116-
include("deprecations.jl")
117118

119+
include("deprecations.jl")
118120
end

src/avl_tree.jl

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
# it has unique keys
2+
# leftChild has keys which are less than the node
3+
# rightChild has keys which are greater than the node
4+
# height stores the height of the subtree.
5+
mutable struct AVLTreeNode{K}
6+
height::Int8
7+
leftChild::Union{AVLTreeNode{K}, Nothing}
8+
rightChild::Union{AVLTreeNode{K}, Nothing}
9+
subsize::Int32
10+
data::K
11+
12+
AVLTreeNode{K}(d::K) where K = new{K}(1, nothing, nothing, 1, d)
13+
end
14+
15+
AVLTreeNode(d) = AVLTreeNode{Any}(d)
16+
17+
AVLTreeNode_or_null{T} = Union{AVLTreeNode{T}, Nothing}
18+
19+
mutable struct AVLTree{T}
20+
root::AVLTreeNode_or_null{T}
21+
count::Int
22+
23+
AVLTree{T}() where T = new{T}(nothing, 0)
24+
end
25+
26+
AVLTree() = AVLTree{Any}()
27+
28+
Base.length(tree::AVLTree) = tree.count
29+
30+
get_height(node::Union{AVLTreeNode, Nothing}) = (node == nothing) ? 0 : node.height
31+
32+
# balance is the difference of height between leftChild and rightChild of a node.
33+
function get_balance(node::Union{AVLTreeNode, Nothing})
34+
if node == nothing
35+
return 0
36+
else
37+
return get_height(node.leftChild) - get_height(node.rightChild)
38+
end
39+
end
40+
41+
# computes the height of the subtree, which basically is
42+
# one added the maximum of the height of the left subtree and right subtree
43+
compute_height(node::AVLTreeNode) = 1 + max(get_height(node.leftChild), get_height(node.rightChild))
44+
45+
get_subsize(node::AVLTreeNode_or_null) = (node == nothing) ? 0 : node.subsize
46+
47+
# compute the subtree size
48+
function compute_subtree_size(node::AVLTreeNode_or_null)
49+
if node == nothing
50+
return 0
51+
else
52+
L = get_subsize(node.leftChild)
53+
R = get_subsize(node.rightChild)
54+
return (L + R + 1)
55+
end
56+
end
57+
58+
"""
59+
left_rotate(node_x::AVLTreeNode)
60+
61+
Performs a left-rotation on `node_x`, updates height of the nodes, and returns the rotated node.
62+
"""
63+
function left_rotate(z::AVLTreeNode)
64+
y = z.rightChild
65+
α = y.leftChild
66+
y.leftChild = z
67+
z.rightChild = α
68+
z.height = compute_height(z)
69+
y.height = compute_height(y)
70+
z.subsize = compute_subtree_size(z)
71+
y.subsize = compute_subtree_size(y)
72+
return y
73+
end
74+
75+
"""
76+
right_rotate(node_x::AVLTreeNode)
77+
78+
Performs a right-rotation on `node_x`, updates height of the nodes, and returns the rotated node.
79+
"""
80+
function right_rotate(z::AVLTreeNode)
81+
y = z.leftChild
82+
α = y.rightChild
83+
y.rightChild = z
84+
z.leftChild = α
85+
z.height = compute_height(z)
86+
y.height = compute_height(y)
87+
z.subsize = compute_subtree_size(z)
88+
y.subsize = compute_subtree_size(y)
89+
return y
90+
end
91+
92+
"""
93+
minimum_node(tree::AVLTree, node::AVLTreeNode)
94+
95+
Returns the AVLTreeNode with minimum value in subtree of `node`.
96+
"""
97+
function minimum_node(node::Union{AVLTreeNode, Nothing})
98+
while node != nothing && node.leftChild != nothing
99+
node = node.leftChild
100+
end
101+
return node
102+
end
103+
104+
function search_node(tree::AVLTree{K}, d::K) where K
105+
prev = nothing
106+
node = tree.root
107+
while node != nothing && node.data != nothing && node.data != d
108+
109+
prev = node
110+
if d < node.data
111+
node = node.leftChild
112+
else
113+
node = node.rightChild
114+
end
115+
end
116+
117+
return (node == nothing) ? prev : node
118+
end
119+
120+
function Base.haskey(tree::AVLTree{K}, d::K) where K
121+
(tree.root == nothing) && return false
122+
node = search_node(tree, d)
123+
return (node.data == d)
124+
end
125+
126+
Base.in(key, tree::AVLTree) = haskey(tree, key)
127+
128+
function Base.insert!(tree::AVLTree{K}, d::K) where K
129+
130+
function insert_node(node::Union{AVLTreeNode, Nothing}, key)
131+
if node == nothing
132+
return AVLTreeNode{K}(key)
133+
elseif key < node.data
134+
node.leftChild = insert_node(node.leftChild, key)
135+
else
136+
node.rightChild = insert_node(node.rightChild, key)
137+
end
138+
139+
node.subsize = compute_subtree_size(node)
140+
node.height = compute_height(node)
141+
balance = get_balance(node)
142+
143+
if balance > 1
144+
if key < node.leftChild.data
145+
return right_rotate(node)
146+
else
147+
node.leftChild = left_rotate(node.leftChild)
148+
return right_rotate(node)
149+
end
150+
end
151+
152+
if balance < -1
153+
if key > node.rightChild.data
154+
return left_rotate(node)
155+
else
156+
node.rightChild = right_rotate(node.rightChild)
157+
return left_rotate(node)
158+
end
159+
end
160+
161+
return node
162+
end
163+
164+
haskey(tree, d) && return tree
165+
166+
tree.root = insert_node(tree.root, d)
167+
tree.count += 1
168+
return tree
169+
end
170+
171+
function Base.push!(tree::AVLTree{K}, key0) where K
172+
key = convert(K, key0)
173+
insert!(tree, key)
174+
end
175+
176+
function Base.delete!(tree::AVLTree{K}, d::K) where K
177+
178+
function delete_node!(node::Union{AVLTreeNode, Nothing}, key)
179+
if key < node.data
180+
node.leftChild = delete_node!(node.leftChild, key)
181+
elseif key > node.data
182+
node.rightChild = delete_node!(node.rightChild, key)
183+
else
184+
if node.leftChild == nothing
185+
result = node.rightChild
186+
return result
187+
elseif node.rightChild == nothing
188+
result = node.leftChild
189+
return result
190+
else
191+
result = minimum_node(node.rightChild)
192+
node.data = result.data
193+
node.rightChild = delete_node!(node.rightChild, result.data)
194+
end
195+
end
196+
197+
node.subsize = compute_subtree_size(node)
198+
node.height = compute_height(node)
199+
balance = get_balance(node)
200+
201+
if balance > 1
202+
if get_balance(node.leftChild) >= 0
203+
return right_rotate(node)
204+
else
205+
node.leftChild = left_rotate(node.leftChild)
206+
return right_rotate(node)
207+
end
208+
end
209+
210+
if balance < -1
211+
if get_balance(node.rightChild) <= 0
212+
return left_rotate(node)
213+
else
214+
node.rightChild = right_rotate(node.rightChild)
215+
return left_rotate(node)
216+
end
217+
end
218+
219+
return node
220+
end
221+
222+
# if the key is not in the tree, do nothing and return the tree
223+
!haskey(tree, d) && return tree
224+
225+
# if the key is present, delete it from the tree
226+
tree.root = delete_node!(tree.root, d)
227+
tree.count -= 1
228+
return tree
229+
end
230+
231+
"""
232+
sorted_rank(tree::AVLTree, key)
233+
234+
Returns the rank of `key` present in the `tree`, if it present. A KeyError is thrown if `key` is not present.
235+
"""
236+
function sorted_rank(tree::AVLTree{K}, key::K) where K
237+
!haskey(tree, key) && throw(KeyError(key))
238+
node = tree.root
239+
rank = 0
240+
while node.data != key
241+
if (node.data < key)
242+
rank += (1 + get_subsize(node.leftChild))
243+
node = node.rightChild
244+
else
245+
node = node.leftChild
246+
end
247+
end
248+
rank += (1 + get_subsize(node.leftChild))
249+
return rank
250+
end
251+
252+
function Base.getindex(tree::AVLTree{K}, ind::Integer) where K
253+
@boundscheck (1 <= ind <= tree.count) || throw(BoundsError("$ind should be in between 1 and $(tree.count)"))
254+
function traverse_tree(node::AVLTreeNode_or_null, idx)
255+
if (node != nothing)
256+
L = get_subsize(node.leftChild)
257+
if idx <= L
258+
return traverse_tree(node.leftChild, idx)
259+
elseif idx == L + 1
260+
return node.data
261+
else
262+
return traverse_tree(node.rightChild, idx - L - 1)
263+
end
264+
end
265+
end
266+
value = traverse_tree(tree.root, ind)
267+
return value
268+
end

test/runtests.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ tests = ["deprecations",
3232
"robin_dict",
3333
"ordered_robin_dict",
3434
"dibit_vector",
35-
"red_black_tree"
35+
"red_black_tree",
36+
"avl_tree"
3637
]
3738

3839
if length(ARGS) > 0

0 commit comments

Comments
 (0)