forked from TheAlgorithms/C-Sharp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAATreeTests.cs
296 lines (254 loc) · 9.8 KB
/
AATreeTests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
using System;
using System.Collections.Generic;
using System.Linq;
using DataStructures.AATree;
using FluentAssertions;
using NUnit.Framework;
namespace DataStructures.Tests;
internal class AaTreeTests
{
[Test]
public void Constructor_UseCustomComparer_FormsCorrectTree()
{
var tree = new AaTree<int>(Comparer<int>.Create((x, y) => y.CompareTo(x)));
tree.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
tree.GetMax().Should().Be(1);
tree.GetMin().Should().Be(10);
tree.GetKeysInOrder().SequenceEqual(new[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }).Should().BeTrue();
Validate(tree.Root);
}
[Test]
public void Add_MultipleKeys_FormsCorrectTree()
{
var tree = new AaTree<int>();
foreach (var elem in new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 })
{
tree.Add(elem);
tree.Count.Should().Be(elem);
tree.Contains(elem).Should().BeTrue();
}
tree.GetKeysInOrder().SequenceEqual(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).Should().BeTrue();
tree.GetKeysPostOrder().SequenceEqual(new[] { 1, 3, 2, 5, 7, 10, 9, 8, 6, 4 }).Should().BeTrue();
Validate(tree.Root);
}
[Test]
public void Add_KeyAlreadyInTree_ThrowsException()
{
var tree = new AaTree<int>();
tree.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
Assert.Throws<ArgumentException>(() => tree.Add(1));
}
[Test]
public void AddRange_MultipleKeys_FormsCorrectTree()
{
var tree = new AaTree<int>();
tree.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
tree.Count.Should().Be(10);
tree.GetKeysInOrder().SequenceEqual(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).Should().BeTrue();
tree.GetKeysPostOrder().SequenceEqual(new[] { 1, 3, 2, 5, 7, 10, 9, 8, 6, 4 }).Should().BeTrue();
Validate(tree.Root);
}
[Test]
public void Remove_MultipleKeys_TreeStillValid()
{
var tree = new AaTree<int>();
tree.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
Remove(4).Should().NotThrow();
tree.Contains(4).Should().BeFalse();
tree.Count.Should().Be(9);
Remove(8).Should().NotThrow();
tree.Contains(8).Should().BeFalse();
tree.Count.Should().Be(8);
Remove(1).Should().NotThrow();
tree.Contains(1).Should().BeFalse();
tree.Count.Should().Be(7);
Validate(tree.Root);
Action Remove(int x) => () => tree.Remove(x);
}
[Test]
public void Remove_KeyNotInTree_Throws()
{
var tree = new AaTree<int>();
tree.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
Action act = () => tree.Remove(999);
act.Should().Throw<InvalidOperationException>();
}
[Test]
public void Remove_EmptyTree_Throws()
{
var tree = new AaTree<int>();
Action act = () => tree.Remove(999);
act.Should().Throw<InvalidOperationException>();
}
[Test]
public void Contains_NonEmptyTree_ReturnsCorrectAnswer()
{
var tree = new AaTree<int>();
tree.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
tree.Contains(6).Should().BeTrue();
tree.Contains(999).Should().BeFalse();
}
[Test]
public void Contains_EmptyTree_ReturnsFalse()
{
var tree = new AaTree<int>();
tree.Contains(999).Should().BeFalse();
}
[Test]
public void GetMax_NonEmptyTree_ReturnsCorrectAnswer()
{
var tree = new AaTree<int>();
tree.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
tree.GetMax().Should().Be(10);
}
[Test]
public void GetMax_EmptyTree_ThrowsCorrectException()
{
var tree = new AaTree<int>();
Assert.Throws<InvalidOperationException>(() => tree.GetMax());
}
[Test]
public void GetMin_NonEmptyTree_ReturnsCorrectAnswer()
{
var tree = new AaTree<int>();
tree.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
tree.GetMin().Should().Be(1);
}
[Test]
public void GetMin_EmptyTree_ThrowsCorrectException()
{
var tree = new AaTree<int>();
Assert.Throws<InvalidOperationException>(() => tree.GetMin());
}
[Test]
public void GetKeysInOrder_NonEmptyTree_ReturnsCorrectAnswer()
{
var tree = new AaTree<int>();
tree.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
tree.GetKeysInOrder().SequenceEqual(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).Should().BeTrue();
}
[Test]
public void GetKeysInOrder_EmptyTree_ReturnsCorrectAnswer()
{
var tree = new AaTree<int>();
tree.GetKeysInOrder().ToList().Count.Should().Be(0);
}
[Test]
public void GetKeysPreOrder_NonEmptyTree_ReturnsCorrectAnswer()
{
var tree = new AaTree<int>();
tree.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
tree.GetKeysPreOrder().SequenceEqual(new[] { 4, 2, 1, 3, 6, 5, 8, 7, 9, 10 })
.Should().BeTrue();
}
[Test]
public void GetKeysPreOrder_EmptyTree_ReturnsCorrectAnswer()
{
var tree = new AaTree<int>();
tree.GetKeysPreOrder().ToList().Count.Should().Be(0);
}
[Test]
public void GetKeysPostOrder_NonEmptyTree_ReturnsCorrectAnswer()
{
var tree = new AaTree<int>();
tree.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
tree.GetKeysPostOrder().SequenceEqual(new[] { 1, 3, 2, 5, 7, 10, 9, 8, 6, 4 })
.Should().BeTrue();
}
[Test]
public void GetKeysPostOrder_EmptyTree_ReturnsCorrectAnswer()
{
var tree = new AaTree<int>();
tree.GetKeysPostOrder().ToList().Count.Should().Be(0);
}
/// <summary>
/// Checks various properties to determine if the tree is a valid AA Tree.
/// Throws exceptions if properties are violated.
/// Useful for debugging.
/// </summary>
/// <remarks>
/// The properties that are checked are:
/// <list type="number">
/// <item>The level of every leaf node is one.</item>
/// <item>The level of every left child is exactly one less than that of its parent.</item>
/// <item>The level of every right child is equal to or one less than that of its parent.</item>
/// <item>The level of every right grandchild is strictly less than that of its grandparent.</item>
/// <item>Every node of level greater than one has two children.</item>
/// </list>
/// More information: https://en.wikipedia.org/wiki/AA_tree .
/// </remarks>
/// <param name="node">The node to check from.</param>
/// <returns>true if node passes all checks, false otherwise.</returns>
private static bool Validate<T>(AaTreeNode<T>? node)
{
if (node is null)
{
return true;
}
// Check level == 1 if node if a leaf node.
var leafNodeCheck = CheckLeafNode(node);
// Check level of left child is exactly one less than parent.
var leftCheck = CheckLeftSubtree(node);
// Check level of right child is equal or one less than parent.
var rightCheck = CheckRightSubtree(node);
// Check right grandchild level is less than node.
var grandchildCheck = CheckRightGrandChild(node);
// Check if node has two children if not leaf.
var nonLeafChildrenCheck = CheckNonLeafChildren(node);
var thisNodeResult = leafNodeCheck && leftCheck && rightCheck;
thisNodeResult = thisNodeResult && grandchildCheck && nonLeafChildrenCheck;
return thisNodeResult && Validate(node.Left) && Validate(node.Right);
}
/// <summary>
/// Checks if node is a leaf, and if so if its level is 1.
/// </summary>
/// <param name="node">The node to check.</param>
/// <returns>true if node passes check, false otherwise.</returns>
private static bool CheckLeafNode<T>(AaTreeNode<T> node)
{
var condition = node.Left is null && node.Right is null && node.Level != 1;
return !condition;
}
/// <summary>
/// Checks if left node's level is exactly one less than node's level.
/// </summary>
/// <param name="node">The node to check.</param>
/// <returns>true if node passes check, false otherwise.</returns>
private static bool CheckLeftSubtree<T>(AaTreeNode<T> node)
{
var condition = node.Left is not null && node.Level - node.Left.Level != 1;
return !condition;
}
/// <summary>
/// Checks if right node's level is either equal to or one less than node's level.
/// </summary>
/// <param name="node">The node to check.</param>
/// <returns>true if node passes check, false otherwise.</returns>
private static bool CheckRightSubtree<T>(AaTreeNode<T> node)
{
var condition = node.Right is not null &&
node.Level - node.Right.Level != 1 &&
node.Level != node.Right.Level;
return !condition;
}
/// <summary>
/// Checks if right grandchild's (right node's right node) level is less than node.
/// </summary>
/// <param name="node">The node to check.</param>
/// <returns>true if node passes check, false otherwise.</returns>
private static bool CheckRightGrandChild<T>(AaTreeNode<T> node)
{
var condition = node.Right?.Right is not null && node.Right.Level < node.Right.Right.Level;
return !condition;
}
/// <summary>
/// Checks if node is not a leaf, and if so if it has two children.
/// </summary>
/// <param name="node">The node to check.</param>
/// <returns>true if node passes check, false otherwise.</returns>
private static bool CheckNonLeafChildren<T>(AaTreeNode<T> node)
{
var condition = node.Level > 1 && (node.Left is null || node.Right is null);
return !condition;
}
}