Skip to content

Commit 2bb69d8

Browse files
committed
Merge branch 'swissdict' of https://github.com/eulerkochy/DataStructures.jl into swissdict
2 parents 6db72e7 + ef3dbae commit 2bb69d8

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
@@ -61,6 +61,7 @@ module DataStructures
6161
export DiBitVector
6262

6363
export RBTree, search_node, minimum_node
64+
export AVLTree, sorted_rank
6465

6566
export findkey
6667

@@ -114,7 +115,8 @@ module DataStructures
114115
export SparseIntSet
115116

116117
include("dibit_vector.jl")
118+
include("avl_tree.jl")
117119
include("red_black_tree.jl")
118-
include("deprecations.jl")
119120

121+
include("deprecations.jl")
120122
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
@@ -33,7 +33,8 @@ tests = ["deprecations",
3333
"ordered_robin_dict",
3434
"dibit_vector",
3535
"red_black_tree",
36-
"swiss_dict"
36+
"swiss_dict",
37+
"avl_tree"
3738
]
3839

3940
if length(ARGS) > 0

0 commit comments

Comments
 (0)