Skip to content

Commit 59e39e9

Browse files
committed
Add insert implementation with illustrations
1 parent 824fa27 commit 59e39e9

File tree

1 file changed

+233
-0
lines changed

1 file changed

+233
-0
lines changed
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
namespace Algorithms.DataStructures
2+
3+
module AVLTree =
4+
5+
type AVLNode = {
6+
Value: int
7+
Height: int
8+
LeftChild: Option<AVLNode>
9+
RightChild: Option<AVLNode>
10+
}
11+
12+
module AVLNode =
13+
let create (value: int) : AVLNode =
14+
{
15+
Value = value
16+
Height = 0
17+
LeftChild = None
18+
RightChild = None
19+
}
20+
21+
let height (maybeNode: Option<AVLNode>) : int =
22+
maybeNode
23+
|> Option.map (fun node -> node.Height)
24+
|> Option.defaultValue -1
25+
26+
let balanceFactor (node: AVLNode) : int =
27+
height node.RightChild - height node.LeftChild
28+
29+
type AVLNode
30+
with
31+
member this.UpdateLeftChild (leftChild: Option<AVLNode>) : AVLNode =
32+
{
33+
this with
34+
LeftChild = leftChild
35+
Height = 1 + max (AVLNode.height leftChild) (AVLNode.height this.RightChild)
36+
}
37+
member this.UpdateRightChild (rightChild: Option<AVLNode>) : AVLNode =
38+
{
39+
this with
40+
RightChild = rightChild
41+
Height = 1 + max (AVLNode.height this.LeftChild) (AVLNode.height rightChild)
42+
}
43+
44+
module AVLTree =
45+
let rotateRight (node: AVLNode) : AVLNode =
46+
// Left child becomes the new root
47+
let leftChild = node.LeftChild |> Option.get
48+
let oldNode = node.UpdateLeftChild (leftChild.RightChild)
49+
leftChild.UpdateRightChild (Some oldNode)
50+
51+
let rotateLeft (node: AVLNode) : AVLNode =
52+
// Right child becomes the new root
53+
let rightChild = node.RightChild |> Option.get
54+
let oldNode = node.UpdateRightChild (rightChild.LeftChild)
55+
rightChild.UpdateLeftChild (Some oldNode)
56+
57+
58+
type AVLTree = {
59+
Root: Option<AVLNode>
60+
}
61+
62+
let insert (value: int) (tree: AVLTree) : AVLTree =
63+
let rec insertImpl (maybeNode: Option<AVLNode>) : AVLNode =
64+
match maybeNode with
65+
| None ->
66+
AVLNode.create value
67+
| Some node ->
68+
let node =
69+
if value < node.Value then
70+
node.UpdateLeftChild (Some (insertImpl node.LeftChild))
71+
elif value = node.Value then
72+
node
73+
else
74+
node.UpdateRightChild (Some (insertImpl node.RightChild))
75+
76+
if AVLNode.balanceFactor node > 1 then
77+
// Root node is right heavy, current balance factor is 2
78+
// check the balance factor of the right child
79+
if node.RightChild |> Option.get |> AVLNode.balanceFactor < 0 then
80+
// Right child is left heavy
81+
// rotate right around right child and rotate left around root
82+
83+
// Illustration: possible heights are shown in brackets,
84+
// and the balance factor of the node is shown in parentheses
85+
86+
// Initial state:
87+
//
88+
// b (+2) [h+3]
89+
// / \
90+
// [h] a f (-1) [h+2]
91+
// /\
92+
// [h+1] (0|-1|+1) d g [h]
93+
// /\
94+
// [h|h-1] c e [h|h-1]
95+
96+
// rotate right around f (right child)
97+
//
98+
// b (+2) [h+3]
99+
// / \
100+
// [h] a d (+1|+2) [h+2]
101+
// /\
102+
// [h|h-1] c f (0|-1) [h+1]
103+
// /\
104+
// [h|h-1] e g [h]
105+
106+
// rotate left around b (root)
107+
// d (0) [h+2]
108+
// __________/\__________
109+
// / \
110+
// [h+1] (0|-1) b f (0|-1) [h+1]
111+
// / \ /\
112+
// [h] a c [h|h-1] [h|h-1] e g [h]
113+
114+
115+
let node =node.UpdateRightChild (Some (AVLTree.rotateRight (node.RightChild |> Option.get)))
116+
AVLTree.rotateLeft node
117+
else
118+
// Right child is balanced or left heavy,
119+
// rotate left around root
120+
121+
// Illustration if right child is balanced
122+
123+
// Initial state:
124+
// b (+2) [h+3]
125+
// / \
126+
// [h] a d (0) [h+2]
127+
// /\
128+
// [h+1] c e [h+1]
129+
130+
// rotate left around b (root)
131+
// d (-1) [h+3]
132+
// / \
133+
// [h+2] (+1) b e [h+1]
134+
// / \
135+
// [h] a c [h+1]
136+
137+
// Illustration if right child is right heavy
138+
139+
// Initial state:
140+
// b (+2) [h+3]
141+
// / \
142+
// [h] a d (+1) [h+2]
143+
// /\
144+
// [h] c e [h+1]
145+
146+
// rotate left around b (root)
147+
// d (0) [h+2]
148+
// / \
149+
// [h+1] (0) b e [h+1]
150+
// / \
151+
// [h] a c [h]
152+
153+
AVLTree.rotateLeft node
154+
elif AVLNode.balanceFactor node < -1 then
155+
// Root node is left heavy, current balance factor is -2
156+
// check the balance factor of the left child
157+
if node.LeftChild |> Option.get |> AVLNode.balanceFactor > 0 then
158+
// Left child is right heavy
159+
// rotate left around left child and rotate right around root
160+
161+
// Initial state:
162+
// f (-2) [h+3]
163+
// / \
164+
// [h+2] (+1) b g [h]
165+
// /\
166+
// [h] a d (0|-1|+1) [h+1]
167+
// /\
168+
// [h|h-1] c e [h|h-1]
169+
170+
// rotate left around b (left child)
171+
// f (-2) [h+3]
172+
// / \
173+
// [h+2] (-2) d g [h]
174+
// / \
175+
// [h+1] b e [h|h-1]
176+
// /\
177+
// [h] a c [h|h-1]
178+
179+
// rotate right around f (root)
180+
// d (0) [h+2]
181+
// __________/\__________
182+
// / \
183+
// [h+1] (0|-1) b f (0|-1) [h+1]
184+
// / \ /\
185+
// [h] a c [h|h-1] [h|h-1] e g [h]
186+
187+
let node = node.UpdateLeftChild (Some (AVLTree.rotateLeft (node.LeftChild |> Option.get)))
188+
AVLTree.rotateRight node
189+
else
190+
// Left child is balanced or left heavy
191+
// rotate right around root
192+
193+
// Illustration if left child is balanced
194+
195+
// Initial state:
196+
// d (-2) [h+3]
197+
// / \
198+
// [h+2] (0) b e [h]
199+
// / \
200+
// [h+1] a c [h+1]
201+
202+
// rotate right around d (root)
203+
// b (+1) [h+3]
204+
// / \
205+
// [h+1] a d (-1) [h+2]
206+
// / \
207+
// [h+1]c e [h]
208+
209+
// Illustration if left child is left heavy
210+
211+
// Initial state:
212+
// d (-2) [h+3]
213+
// / \
214+
// [h+2] (-1) b e [h]
215+
// / \
216+
// [h+1] a c [h]
217+
218+
// rotate right around d (root)
219+
// b (0) [h+2]
220+
// / \
221+
// [h+1] a d (0) [h+1]
222+
// / \
223+
// [h] c e [h]
224+
225+
AVLTree.rotateRight node
226+
else
227+
// Balance of root is within acceptable range
228+
node
229+
230+
231+
insertImpl tree.Root
232+
|> fun root -> { Root = Some root }
233+

0 commit comments

Comments
 (0)