Files
TheAlgorithms-PHP/tests/DataStructures/AVLTreeTest.php
Ramy e43b4bfec8 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 <bbj1979@gmail.com>

* Update DataStructures/DisjointSets/DisjointSetNode.php

Co-authored-by: Brandon Johnson <bbj1979@gmail.com>

* Update DataStructures/DisjointSets/DisjointSetNode.php

Co-authored-by: Brandon Johnson <bbj1979@gmail.com>

* Update tests/DataStructures/DisjointSetTest.php

Co-authored-by: Brandon Johnson <bbj1979@gmail.com>

* Update tests/DataStructures/DisjointSetTest.php

Co-authored-by: Brandon Johnson <bbj1979@gmail.com>

* Update tests/DataStructures/DisjointSetTest.php

Co-authored-by: Brandon Johnson <bbj1979@gmail.com>

* 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 <bbj1979@gmail.com>
Co-authored-by: Ramy-Badr-Ahmed <Ramy-Badr-Ahmed@users.noreply.github.com>
2024-09-17 23:07:12 -06:00

296 lines
9.5 KiB
PHP

<?php
namespace DataStructures;
require_once __DIR__ . '/../../DataStructures/AVLTree/AVLTree.php';
require_once __DIR__ . '/../../DataStructures/AVLTree/AVLTreeNode.php';
require_once __DIR__ . '/../../DataStructures/AVLTree/TreeTraversal.php';
use DataStructures\AVLTree\AVLTree;
use DataStructures\AVLTree\TreeTraversal;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionException;
class AVLTreeTest extends TestCase
{
private AVLTree $tree;
protected function setUp(): void
{
$this->tree = new AVLTree();
}
private function populateTree(): void
{
$this->tree->insert(10, 'Value 10');
$this->tree->insert(20, 'Value 20');
$this->tree->insert(5, 'Value 5');
$this->tree->insert(15, 'Value 15');
}
public function testInsertAndSearch(): void
{
$this->populateTree();
$this->assertEquals('Value 10', $this->tree->search(10), 'Value for key 10 should be "Value 10"');
$this->assertEquals('Value 20', $this->tree->search(20), 'Value for key 20 should be "Value 20"');
$this->assertEquals('Value 5', $this->tree->search(5), 'Value for key 5 should be "Value 5"');
$this->assertNull($this->tree->search(25), 'Value for non-existent key 25 should be null');
}
public function testDelete(): void
{
$this->populateTree();
$this->tree->delete(20);
$this->tree->delete(5);
$this->assertNull($this->tree->search(20), 'Value for deleted key 20 should be null');
$this->assertNull($this->tree->search(5), 'Value for deleted key 5 should be null');
$this->tree->delete(50);
$this->assertNotNull($this->tree->search(10), 'Value for key 10 should still exist');
$this->assertNotNull($this->tree->search(15), 'Value for key 15 should still exist');
$this->assertNull($this->tree->search(50), 'Value for non-existent key 50 should be null');
$expectedInOrderAfterDelete = [
[10 => 'Value 10'],
[15 => 'Value 15']
];
$result = TreeTraversal::inOrder($this->tree->getRoot());
$this->assertEquals(
$expectedInOrderAfterDelete,
$result,
'In-order traversal after deletion should match expected result'
);
}
public function testInOrderTraversal(): void
{
$this->populateTree();
$expectedInOrder = [
[5 => 'Value 5'],
[10 => 'Value 10'],
[15 => 'Value 15'],
[20 => 'Value 20']
];
$result = $this->tree->inOrderTraversal();
$this->assertEquals($expectedInOrder, $result, 'In-order traversal should match expected result');
}
public function testPreOrderTraversal(): void
{
$this->populateTree();
$expectedPreOrder = [
[10 => 'Value 10'],
[5 => 'Value 5'],
[20 => 'Value 20'],
[15 => 'Value 15']
];
$result = $this->tree->preOrderTraversal();
$this->assertEquals($expectedPreOrder, $result, 'Pre-order traversal should match expected result');
}
public function testPostOrderTraversal(): void
{
$this->populateTree();
$expectedPostOrder = [
[5 => 'Value 5'],
[15 => 'Value 15'],
[20 => 'Value 20'],
[10 => 'Value 10']
];
$result = TreeTraversal::postOrder($this->tree->getRoot());
$this->assertEquals($expectedPostOrder, $result, 'Post-order traversal should match expected result');
}
public function testBreadthFirstTraversal(): void
{
$this->populateTree();
$expectedBFT = [
[10 => 'Value 10'],
[5 => 'Value 5'],
[20 => 'Value 20'],
[15 => 'Value 15']
];
$result = TreeTraversal::breadthFirst($this->tree->getRoot());
$this->assertEquals($expectedBFT, $result, 'Breadth-first traversal should match expected result');
}
public function testInsertAndDeleteSingleNode(): void
{
$this->tree = new AVLTree();
$this->tree->insert(1, 'One');
$this->assertEquals('One', $this->tree->search(1), 'Value for key 1 should be "One"');
$this->tree->delete(1);
$this->assertNull($this->tree->search(1), 'Value for key 1 should be null after deletion');
}
public function testDeleteFromEmptyTree(): void
{
$this->tree = new AVLTree();
$this->tree->delete(1);
$this->assertNull($this->tree->search(1), 'Value for key 1 should be null as it was never inserted');
}
public function testInsertDuplicateKeys(): void
{
$this->tree = new AVLTree();
$this->tree->insert(1, 'One');
$this->tree->insert(1, 'One Updated');
$this->assertEquals(
'One Updated',
$this->tree->search(1),
'Value for key 1 should be "One Updated" after updating'
);
}
public function testLargeTree(): void
{
// Inserting a large number of nodes
for ($i = 1; $i <= 1000; $i++) {
$this->tree->insert($i, "Value $i");
}
// Verify that all inserted nodes can be searched
for ($i = 1; $i <= 1000; $i++) {
$this->assertEquals("Value $i", $this->tree->search($i), "Value for key $i should be 'Value $i'");
}
// Verify that all inserted nodes can be deleted
for ($i = 1; $i <= 5; $i++) {
$this->tree->delete($i);
$this->assertNull($this->tree->search($i), "Value for key $i should be null after deletion");
}
}
public function testBalance(): void
{
$this->populateTree();
// Perform operations that may unbalance the tree
$this->tree->insert(30, 'Value 30');
$this->tree->insert(25, 'Value 25');
// After insertions, check the balance
$this->assertTrue($this->tree->isBalanced(), 'Tree should be balanced after insertions');
}
public function testRightRotation(): void
{
$this->populateTreeForRightRotation();
// Insert a node that will trigger a right rotation
$this->tree->insert(40, 'Value 40');
// Verify the tree structure after rotation
$root = $this->tree->getRoot();
$this->assertEquals(20, $root->key, 'Root should be 20 after right rotation');
$this->assertEquals(10, $root->left->key, 'Left child of root should be 10');
$this->assertEquals(30, $root->right->key, 'Right child of root should be 30');
}
private function populateTreeForRightRotation(): void
{
// Insert nodes in a way that requires a right rotation
$this->tree->insert(10, 'Value 10');
$this->tree->insert(20, 'Value 20');
$this->tree->insert(30, 'Value 30'); // This should trigger a right rotation around 10
}
public function testLeftRotation(): void
{
$this->populateTreeForLeftRotation();
// Insert a node that will trigger a left rotation
$this->tree->insert(5, 'Value 5');
// Verify the tree structure after rotation
$root = $this->tree->getRoot();
$this->assertEquals(20, $root->key, 'Root should be 20 after left rotation');
$this->assertEquals(10, $root->left->key, 'Left child of root should be 10');
$this->assertEquals(30, $root->right->key, 'Right child of root should be 30');
}
private function populateTreeForLeftRotation(): void
{
$this->tree->insert(30, 'Value 30');
$this->tree->insert(20, 'Value 20');
$this->tree->insert(10, 'Value 10'); // This should trigger a left rotation around 30
}
/**
* @throws ReflectionException
*/
public function testGetMinNode(): void
{
$this->populateTree();
// Using Reflection to access the private getMinNode method
$reflection = new ReflectionClass($this->tree);
$method = $reflection->getMethod('getMinNode');
$method->setAccessible(true);
$minNode = $method->invoke($this->tree, $this->tree->getRoot());
// Verify the minimum node
$this->assertEquals(5, $minNode->key, 'Minimum key in the tree should be 5');
$this->assertEquals('Value 5', $minNode->value, 'Value for minimum key 5 should be "Value 5"');
}
public function testSizeAfterInsertions(): void
{
$this->tree = new AVLTree();
$this->assertEquals(0, $this->tree->size(), 'Size should be 0 initially');
$this->tree->insert(10, 'Value 10');
$this->tree->insert(20, 'Value 20');
$this->tree->insert(5, 'Value 5');
$this->assertEquals(3, $this->tree->size(), 'Size should be 3 after 3 insertions');
$this->tree->delete(20);
$this->assertEquals(2, $this->tree->size(), 'Size should be 2 after deleting 1 node');
}
public function testSizeAfterMultipleInsertionsAndDeletions(): void
{
$this->tree = new AVLTree();
// Insert nodes
for ($i = 1; $i <= 10; $i++) {
$this->tree->insert($i, "Value $i");
}
$this->assertEquals(10, $this->tree->size(), 'Size should be 10 after 10 insertions');
for ($i = 1; $i <= 5; $i++) {
$this->tree->delete($i);
}
$this->assertEquals(5, $this->tree->size(), 'Size should be 5 after deleting 5 nodes');
}
public function testSizeOnEmptyTree(): void
{
$this->tree = new AVLTree();
$this->assertEquals(0, $this->tree->size(), 'Size should be 0 for an empty tree');
}
}