Skip to content

Commit e43b4bf

Browse files
Implemented AVL Tree Data Structure (#163)
* Added Disjoint Sets Data structure * Moved DisjointSetTest.php to tests/DataStructures * Update DataStructures/DisjointSets/DisjointSet.php Co-authored-by: Brandon Johnson <[email protected]> * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson <[email protected]> * Update DataStructures/DisjointSets/DisjointSetNode.php Co-authored-by: Brandon Johnson <[email protected]> * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson <[email protected]> * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson <[email protected]> * Update tests/DataStructures/DisjointSetTest.php Co-authored-by: Brandon Johnson <[email protected]> * Considered PHPCS remarks. Unit Testing is now working. * Remove data type mixed. Considered annotations for php7.4. * Remove data type mixed. Considered annotations for php7.4. * updating DIRECTORY.md * Implemented AVLTree DataStructure * Implemented AVLTree DataStructure --------- Co-authored-by: Brandon Johnson <[email protected]> Co-authored-by: Ramy-Badr-Ahmed <[email protected]>
1 parent 8c808cd commit e43b4bf

File tree

5 files changed

+726
-0
lines changed

5 files changed

+726
-0
lines changed

DIRECTORY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
* [Speedconversion](./Conversions/SpeedConversion.php)
1818

1919
## Datastructures
20+
* AVLTree
21+
* [AVLTree](./DataStructures/AVLTree/AVLTree.php)
22+
* [AVLTreeNode](./DataStructures/AVLTree/AVLTreeNode.php)
2023
* Disjointsets
2124
* [Disjointset](./DataStructures/DisjointSets/DisjointSet.php)
2225
* [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php)
@@ -117,6 +120,7 @@
117120
* Conversions
118121
* [Conversionstest](./tests/Conversions/ConversionsTest.php)
119122
* Datastructures
123+
* [AVLTreeTest](./tests/DataStructures/AVLTreeTest.php)
120124
* [Disjointsettest](./tests/DataStructures/DisjointSetTest.php)
121125
* [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php)
122126
* [Queuetest](./tests/DataStructures/QueueTest.php)

DataStructures/AVLTree/AVLTree.php

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
<?php
2+
3+
namespace DataStructures\AVLTree;
4+
5+
/**
6+
* Class AVLTree
7+
* Implements an AVL Tree data structure with self-balancing capability.
8+
*/
9+
class AVLTree
10+
{
11+
private ?AVLTreeNode $root;
12+
private int $counter;
13+
14+
public function __construct()
15+
{
16+
$this->root = null;
17+
$this->counter = 0;
18+
}
19+
20+
/**
21+
* Get the root node of the AVL Tree.
22+
*/
23+
public function getRoot(): ?AVLTreeNode
24+
{
25+
return $this->root;
26+
}
27+
28+
/**
29+
* Retrieve a node by its key.
30+
*
31+
* @param mixed $key The key of the node to retrieve.
32+
* @return ?AVLTreeNode The node with the specified key, or null if not found.
33+
*/
34+
public function getNode($key): ?AVLTreeNode
35+
{
36+
return $this->searchNode($this->root, $key);
37+
}
38+
39+
/**
40+
* Get the number of nodes in the AVL Tree.
41+
*/
42+
public function size(): int
43+
{
44+
return $this->counter;
45+
}
46+
47+
/**
48+
* Insert a key-value pair into the AVL Tree.
49+
*
50+
* @param mixed $key The key to insert.
51+
* @param mixed $value The value associated with the key.
52+
*/
53+
public function insert($key, $value): void
54+
{
55+
$this->root = $this->insertNode($this->root, $key, $value);
56+
$this->counter++;
57+
}
58+
59+
/**
60+
* Delete a node by its key from the AVL Tree.
61+
*
62+
* @param mixed $key The key of the node to delete.
63+
*/
64+
public function delete($key): void
65+
{
66+
$this->root = $this->deleteNode($this->root, $key);
67+
$this->counter--;
68+
}
69+
70+
/**
71+
* Search for a value by its key.
72+
*
73+
* @param mixed $key The key to search for.
74+
* @return mixed The value associated with the key, or null if not found.
75+
*/
76+
public function search($key)
77+
{
78+
$node = $this->searchNode($this->root, $key);
79+
return $node ? $node->value : null;
80+
}
81+
82+
/**
83+
* Perform an in-order traversal of the AVL Tree.
84+
* Initiates the traversal on the root node directly and returns the array of key-value pairs.
85+
*/
86+
public function inOrderTraversal(): array
87+
{
88+
return TreeTraversal::inOrder($this->root);
89+
}
90+
91+
/**
92+
* Perform a pre-order traversal of the AVL Tree.
93+
* Initiates the traversal on the root node directly and returns the array of key-value pairs.
94+
*/
95+
public function preOrderTraversal(): array
96+
{
97+
return TreeTraversal::preOrder($this->root);
98+
}
99+
100+
/**
101+
* Perform a post-order traversal of the AVL Tree.
102+
* Initiates the traversal on the root node directly and returns the array of key-value pairs.
103+
*/
104+
public function postOrderTraversal(): array
105+
{
106+
return TreeTraversal::postOrder($this->root);
107+
}
108+
109+
/**
110+
* Perform a breadth-first traversal of the AVL Tree.
111+
*/
112+
public function breadthFirstTraversal(): array
113+
{
114+
return TreeTraversal::breadthFirst($this->root);
115+
}
116+
117+
/**
118+
* Check if the AVL Tree is balanced.
119+
* This method check balance starting from the root node directly
120+
*/
121+
public function isBalanced(): bool
122+
{
123+
return $this->isBalancedHelper($this->root);
124+
}
125+
126+
/**
127+
* Insert a node into the AVL Tree and balance the tree.
128+
*
129+
* @param ?AVLTreeNode $node The current node.
130+
* @param mixed $key The key to insert.
131+
* @param mixed $value The value to insert.
132+
* @return AVLTreeNode The new root of the subtree.
133+
*/
134+
private function insertNode(?AVLTreeNode $node, $key, $value): AVLTreeNode
135+
{
136+
if ($node === null) {
137+
return new AVLTreeNode($key, $value);
138+
}
139+
140+
if ($key < $node->key) {
141+
$node->left = $this->insertNode($node->left, $key, $value);
142+
} elseif ($key > $node->key) {
143+
$node->right = $this->insertNode($node->right, $key, $value);
144+
} else {
145+
$node->value = $value; // Update existing value
146+
}
147+
148+
$node->updateHeight();
149+
return $this->balance($node);
150+
}
151+
152+
/**
153+
* Delete a node by its key and balance the tree.
154+
*
155+
* @param ?AVLTreeNode $node The current node.
156+
* @param mixed $key The key of the node to delete.
157+
* @return ?AVLTreeNode The new root of the subtree.
158+
*/
159+
private function deleteNode(?AVLTreeNode $node, $key): ?AVLTreeNode
160+
{
161+
if ($node === null) {
162+
return null;
163+
}
164+
165+
if ($key < $node->key) {
166+
$node->left = $this->deleteNode($node->left, $key);
167+
} elseif ($key > $node->key) {
168+
$node->right = $this->deleteNode($node->right, $key);
169+
} else {
170+
if (!$node->left) {
171+
return $node->right;
172+
}
173+
if (!$node->right) {
174+
return $node->left;
175+
}
176+
177+
$minNode = $this->getMinNode($node->right);
178+
$node->key = $minNode->key;
179+
$node->value = $minNode->value;
180+
$node->right = $this->deleteNode($node->right, $minNode->key);
181+
}
182+
183+
$node->updateHeight();
184+
return $this->balance($node);
185+
}
186+
187+
/**
188+
* Search for a node by its key.
189+
*
190+
* @param ?AVLTreeNode $node The current node.
191+
* @param mixed $key The key to search for.
192+
* @return ?AVLTreeNode The node with the specified key, or null if not found.
193+
*/
194+
private function searchNode(?AVLTreeNode $node, $key): ?AVLTreeNode
195+
{
196+
if ($node === null) {
197+
return null;
198+
}
199+
200+
if ($key < $node->key) {
201+
return $this->searchNode($node->left, $key);
202+
} elseif ($key > $node->key) {
203+
return $this->searchNode($node->right, $key);
204+
} else {
205+
return $node;
206+
}
207+
}
208+
209+
/**
210+
* Helper method to check if a subtree is balanced.
211+
*
212+
* @param ?AVLTreeNode $node The current node.
213+
* @return bool True if the subtree is balanced, false otherwise.
214+
*/
215+
private function isBalancedHelper(?AVLTreeNode $node): bool
216+
{
217+
if ($node === null) {
218+
return true;
219+
}
220+
221+
$leftHeight = $node->left ? $node->left->height : 0;
222+
$rightHeight = $node->right ? $node->right->height : 0;
223+
224+
$balanceFactor = abs($leftHeight - $rightHeight);
225+
if ($balanceFactor > 1) {
226+
return false;
227+
}
228+
229+
return $this->isBalancedHelper($node->left) && $this->isBalancedHelper($node->right);
230+
}
231+
232+
/**
233+
* Balance the subtree rooted at the given node.
234+
*
235+
* @param ?AVLTreeNode $node The current node.
236+
* @return ?AVLTreeNode The new root of the subtree.
237+
*/
238+
private function balance(?AVLTreeNode $node): ?AVLTreeNode
239+
{
240+
if ($node->balanceFactor() > 1) {
241+
if ($node->left && $node->left->balanceFactor() < 0) {
242+
$node->left = $this->rotateLeft($node->left);
243+
}
244+
return $this->rotateRight($node);
245+
}
246+
247+
if ($node->balanceFactor() < -1) {
248+
if ($node->right && $node->right->balanceFactor() > 0) {
249+
$node->right = $this->rotateRight($node->right);
250+
}
251+
return $this->rotateLeft($node);
252+
}
253+
254+
return $node;
255+
}
256+
257+
/**
258+
* Perform a left rotation on the given node.
259+
*
260+
* @param AVLTreeNode $node The node to rotate.
261+
* @return AVLTreeNode The new root of the rotated subtree.
262+
*/
263+
private function rotateLeft(AVLTreeNode $node): AVLTreeNode
264+
{
265+
$newRoot = $node->right;
266+
$node->right = $newRoot->left;
267+
$newRoot->left = $node;
268+
269+
$node->updateHeight();
270+
$newRoot->updateHeight();
271+
272+
return $newRoot;
273+
}
274+
275+
/**
276+
* Perform a right rotation on the given node.
277+
*
278+
* @param AVLTreeNode $node The node to rotate.
279+
* @return AVLTreeNode The new root of the rotated subtree.
280+
*/
281+
private function rotateRight(AVLTreeNode $node): AVLTreeNode
282+
{
283+
$newRoot = $node->left;
284+
$node->left = $newRoot->right;
285+
$newRoot->right = $node;
286+
287+
$node->updateHeight();
288+
$newRoot->updateHeight();
289+
290+
return $newRoot;
291+
}
292+
293+
/**
294+
* Get the node with the minimum key in the given subtree.
295+
*
296+
* @param AVLTreeNode $node The root of the subtree.
297+
* @return AVLTreeNode The node with the minimum key.
298+
*/
299+
private function getMinNode(AVLTreeNode $node): AVLTreeNode
300+
{
301+
while ($node->left) {
302+
$node = $node->left;
303+
}
304+
return $node;
305+
}
306+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace DataStructures\AVLTree;
4+
5+
class AVLTreeNode
6+
{
7+
/**
8+
* @var int|string
9+
*/
10+
public $key;
11+
/**
12+
* @var mixed
13+
*/
14+
public $value;
15+
public ?AVLTreeNode $left;
16+
public ?AVLTreeNode $right;
17+
public int $height;
18+
19+
public function __construct($key, $value, ?AVLTreeNode $left = null, ?AVLTreeNode $right = null)
20+
{
21+
$this->key = $key;
22+
$this->value = $value;
23+
$this->left = $left;
24+
$this->right = $right;
25+
$this->height = 1; // New node is initially at height 1
26+
}
27+
28+
public function updateHeight(): void
29+
{
30+
$leftHeight = $this->left ? $this->left->height : 0;
31+
$rightHeight = $this->right ? $this->right->height : 0;
32+
$this->height = max($leftHeight, $rightHeight) + 1;
33+
}
34+
35+
public function balanceFactor(): int
36+
{
37+
$leftHeight = $this->left ? $this->left->height : 0;
38+
$rightHeight = $this->right ? $this->right->height : 0;
39+
return $leftHeight - $rightHeight;
40+
}
41+
}

0 commit comments

Comments
 (0)