diff --git a/src/Map/TreeMap.php b/src/Map/TreeMap.php index 9ef5d3f..b45b6ee 100644 --- a/src/Map/TreeMap.php +++ b/src/Map/TreeMap.php @@ -6,7 +6,7 @@ use KaririCode\Contract\DataStructure\Behavioral\IterableCollection; use KaririCode\Contract\DataStructure\Map; -use KaririCode\DataStructure\TreeMapNode; +use KaririCode\DataStructure\Tree\TreeMapNode; /** * TreeMap implementation. @@ -206,7 +206,7 @@ private function balanceAfterInsertion(TreeMapNode $node): void } } } - $this->root->setBlack(); + $this->root?->setBlack(); } private function rotateLeft(TreeMapNode $node): void diff --git a/src/TreeMapNode.php b/src/Tree/TreeMapNode.php similarity index 98% rename from src/TreeMapNode.php rename to src/Tree/TreeMapNode.php index 3897915..6031cff 100644 --- a/src/TreeMapNode.php +++ b/src/Tree/TreeMapNode.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace KaririCode\DataStructure; +namespace KaririCode\DataStructure\Tree; /** * TreeMapNode class. diff --git a/src/Tree/TreeNode.php b/src/Tree/TreeNode.php new file mode 100644 index 0000000..1b69fde --- /dev/null +++ b/src/Tree/TreeNode.php @@ -0,0 +1,86 @@ +left = $node; + if (null !== $node) { + $node->parent = $this; + } + } + + /** + * Sets the right child of this node and updates the parent reference of the child node. + */ + public function setRight(?TreeNode $node): void + { + $this->right = $node; + if (null !== $node) { + $node->parent = $this; + } + } + + /** + * Removes this node from its parent node, detaching it from the tree. + */ + public function removeFromParent(): void + { + if (null !== $this->parent) { + if ($this === $this->parent->left) { + $this->parent->left = null; + } else { + $this->parent->right = null; + } + $this->parent = null; + } + } + + /** + * Replaces this node with the specified replacement node. + * + * @param TreeNode $replacement the node that will replace the current node + * + * @throws \RuntimeException if trying to replace the root node without a parent + */ + public function replaceWith(TreeNode $replacement): void + { + if (null === $this->parent) { + throw new \RuntimeException('Cannot replace root node'); + } + if ($this === $this->parent->left) { + $this->parent->setLeft($replacement); + } else { + $this->parent->setRight($replacement); + } + } +} diff --git a/src/TreeNode.php b/src/TreeNode.php deleted file mode 100644 index e69de29..0000000 diff --git a/tests/Map/TreeMapTest.php b/tests/Map/TreeMapTest.php index 6be16e2..c4a3cbb 100644 --- a/tests/Map/TreeMapTest.php +++ b/tests/Map/TreeMapTest.php @@ -5,7 +5,7 @@ namespace KaririCode\DataStructure\Tests\Map; use KaririCode\DataStructure\Map\TreeMap; -use KaririCode\DataStructure\TreeMapNode; +use KaririCode\DataStructure\Tree\TreeMapNode; use PHPUnit\Framework\TestCase; final class TreeMapTest extends TestCase diff --git a/tests/TreeMapNodeTest.php b/tests/Tree/TreeMapNodeTest.php similarity index 96% rename from tests/TreeMapNodeTest.php rename to tests/Tree/TreeMapNodeTest.php index 66087a5..c60167c 100644 --- a/tests/TreeMapNodeTest.php +++ b/tests/Tree/TreeMapNodeTest.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace KaririCode\DataStructure\Tests\Map; +namespace KaririCode\DataStructure\Tests\Tree; -use KaririCode\DataStructure\TreeMapNode; +use KaririCode\DataStructure\Tree\TreeMapNode; use PHPUnit\Framework\TestCase; final class TreeMapNodeTest extends TestCase diff --git a/tests/Tree/TreeNodeTest.php b/tests/Tree/TreeNodeTest.php new file mode 100644 index 0000000..8e490a2 --- /dev/null +++ b/tests/Tree/TreeNodeTest.php @@ -0,0 +1,118 @@ +assertSame(1, $node->key); + $this->assertSame('valor1', $node->value); + $this->assertNull($node->left); + $this->assertNull($node->right); + $this->assertNull($node->parent); + } + + public function testSetLeftChild(): void + { + $parent = new TreeNode(1, 'parent'); + $child = new TreeNode(2, 'child'); + + $parent->setLeft($child); + + $this->assertSame($child, $parent->left); + $this->assertSame($parent, $child->parent); + } + + public function testSetRightChild(): void + { + $parent = new TreeNode(1, 'parent'); + $child = new TreeNode(2, 'child'); + + $parent->setRight($child); + + $this->assertSame($child, $parent->right); + $this->assertSame($parent, $child->parent); + } + + public function testRemoveFromParent(): void + { + $parent = new TreeNode(1, 'parent'); + $child = new TreeNode(2, 'child'); + + $parent->setLeft($child); + $child->removeFromParent(); + + $this->assertNull($parent->left); + $this->assertNull($child->parent); + } + + public function testReplaceWith(): void + { + $parent = new TreeNode(1, 'parent'); + $child = new TreeNode(2, 'child'); + $replacement = new TreeNode(3, 'replacement'); + + $parent->setLeft($child); + $child->replaceWith($replacement); + + $this->assertSame($replacement, $parent->left); + $this->assertSame($parent, $replacement->parent); + } + + public function testSetRightAndLeftTogether(): void + { + $parent = new TreeNode(1, 'parent'); + $leftChild = new TreeNode(2, 'left'); + $rightChild = new TreeNode(3, 'right'); + + $parent->setLeft($leftChild); + $parent->setRight($rightChild); + + $this->assertSame($leftChild, $parent->left); + $this->assertSame($rightChild, $parent->right); + $this->assertSame($parent, $leftChild->parent); + $this->assertSame($parent, $rightChild->parent); + } + + public function testReplaceWithThrowsExceptionWhenNodeIsRoot(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Cannot replace root node'); + + $root = new TreeNode(1, 'root'); + $replacement = new TreeNode(2, 'replacement'); + + $root->replaceWith($replacement); + } + + public function testRemoveFromParentRightChild(): void + { + $parent = new TreeNode(1, 'parent'); + $child = new TreeNode(2, 'child'); + + $parent->setRight($child); + $child->removeFromParent(); + + $this->assertNull($parent->right); + $this->assertNull($child->parent); + } + + public function testReplaceWithRightChild(): void + { + $parent = new TreeNode(1, 'parent'); + $child = new TreeNode(2, 'child'); + $replacement = new TreeNode(3, 'replacement'); + + $parent->setRight($child); + $child->replaceWith($replacement); + + $this->assertSame($replacement, $parent->right); + $this->assertSame($parent, $replacement->parent); + } +}