Skip to content

Commit 8cc515e

Browse files
committed
refactor(Tree): Move TreeMapNode to Tree directory and add TreeNode with tests
- Moved TreeMapNode class to the Tree directory for better structure. - Created TreeNode.php with full implementation. - Added comprehensive tests for TreeNode, covering left and right child handling, parent removal, and node replacement. - Ensured 100% test coverage for TreeNode functionality.
1 parent 1f81d65 commit 8cc515e

File tree

7 files changed

+210
-6
lines changed

7 files changed

+210
-6
lines changed

src/Map/TreeMap.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use KaririCode\Contract\DataStructure\Behavioral\IterableCollection;
88
use KaririCode\Contract\DataStructure\Map;
9-
use KaririCode\DataStructure\TreeMapNode;
9+
use KaririCode\DataStructure\Tree\TreeMapNode;
1010

1111
/**
1212
* TreeMap implementation.
@@ -206,7 +206,7 @@ private function balanceAfterInsertion(TreeMapNode $node): void
206206
}
207207
}
208208
}
209-
$this->root->setBlack();
209+
$this->root?->setBlack();
210210
}
211211

212212
private function rotateLeft(TreeMapNode $node): void

src/TreeMapNode.php renamed to src/Tree/TreeMapNode.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace KaririCode\DataStructure;
5+
namespace KaririCode\DataStructure\Tree;
66

77
/**
88
* TreeMapNode class.

src/Tree/TreeNode.php

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\DataStructure\Tree;
6+
7+
/**
8+
* TreeNode class.
9+
*
10+
* This class represents a generic node in a binary tree, which can be used for various tree structures.
11+
* Each node contains a key, a value, and references to its left and right child nodes, as well as an optional parent node.
12+
*
13+
* @category Data Structures
14+
*
15+
* @author Walmir Silva
16+
* @license MIT
17+
*
18+
* @see https://kariricode.org/
19+
*/
20+
class TreeNode
21+
{
22+
public function __construct(
23+
public mixed $key,
24+
public mixed $value,
25+
public ?TreeNode $left = null,
26+
public ?TreeNode $right = null,
27+
public ?TreeNode $parent = null
28+
) {
29+
}
30+
31+
/**
32+
* Sets the left child of this node and updates the parent reference of the child node.
33+
*/
34+
public function setLeft(?TreeNode $node): void
35+
{
36+
$this->left = $node;
37+
if (null !== $node) {
38+
$node->parent = $this;
39+
}
40+
}
41+
42+
/**
43+
* Sets the right child of this node and updates the parent reference of the child node.
44+
*/
45+
public function setRight(?TreeNode $node): void
46+
{
47+
$this->right = $node;
48+
if (null !== $node) {
49+
$node->parent = $this;
50+
}
51+
}
52+
53+
/**
54+
* Removes this node from its parent node, detaching it from the tree.
55+
*/
56+
public function removeFromParent(): void
57+
{
58+
if (null !== $this->parent) {
59+
if ($this === $this->parent->left) {
60+
$this->parent->left = null;
61+
} else {
62+
$this->parent->right = null;
63+
}
64+
$this->parent = null;
65+
}
66+
}
67+
68+
/**
69+
* Replaces this node with the specified replacement node.
70+
*
71+
* @param TreeNode $replacement the node that will replace the current node
72+
*
73+
* @throws \RuntimeException if trying to replace the root node without a parent
74+
*/
75+
public function replaceWith(TreeNode $replacement): void
76+
{
77+
if (null === $this->parent) {
78+
throw new \RuntimeException('Cannot replace root node');
79+
}
80+
if ($this === $this->parent->left) {
81+
$this->parent->setLeft($replacement);
82+
} else {
83+
$this->parent->setRight($replacement);
84+
}
85+
}
86+
}

src/TreeNode.php

Whitespace-only changes.

tests/Map/TreeMapTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace KaririCode\DataStructure\Tests\Map;
66

77
use KaririCode\DataStructure\Map\TreeMap;
8-
use KaririCode\DataStructure\TreeMapNode;
8+
use KaririCode\DataStructure\Tree\TreeMapNode;
99
use PHPUnit\Framework\TestCase;
1010

1111
final class TreeMapTest extends TestCase

tests/TreeMapNodeTest.php renamed to tests/Tree/TreeMapNodeTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
declare(strict_types=1);
44

5-
namespace KaririCode\DataStructure\Tests\Map;
5+
namespace KaririCode\DataStructure\Tests\Tree;
66

7-
use KaririCode\DataStructure\TreeMapNode;
7+
use KaririCode\DataStructure\Tree\TreeMapNode;
88
use PHPUnit\Framework\TestCase;
99

1010
final class TreeMapNodeTest extends TestCase

tests/Tree/TreeNodeTest.php

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use KaririCode\DataStructure\Tree\TreeNode;
6+
use PHPUnit\Framework\TestCase;
7+
8+
final class TreeNodeTest extends TestCase
9+
{
10+
public function testCreateNode(): void
11+
{
12+
$node = new TreeNode(1, 'valor1');
13+
14+
$this->assertSame(1, $node->key);
15+
$this->assertSame('valor1', $node->value);
16+
$this->assertNull($node->left);
17+
$this->assertNull($node->right);
18+
$this->assertNull($node->parent);
19+
}
20+
21+
public function testSetLeftChild(): void
22+
{
23+
$parent = new TreeNode(1, 'parent');
24+
$child = new TreeNode(2, 'child');
25+
26+
$parent->setLeft($child);
27+
28+
$this->assertSame($child, $parent->left);
29+
$this->assertSame($parent, $child->parent);
30+
}
31+
32+
public function testSetRightChild(): void
33+
{
34+
$parent = new TreeNode(1, 'parent');
35+
$child = new TreeNode(2, 'child');
36+
37+
$parent->setRight($child);
38+
39+
$this->assertSame($child, $parent->right);
40+
$this->assertSame($parent, $child->parent);
41+
}
42+
43+
public function testRemoveFromParent(): void
44+
{
45+
$parent = new TreeNode(1, 'parent');
46+
$child = new TreeNode(2, 'child');
47+
48+
$parent->setLeft($child);
49+
$child->removeFromParent();
50+
51+
$this->assertNull($parent->left);
52+
$this->assertNull($child->parent);
53+
}
54+
55+
public function testReplaceWith(): void
56+
{
57+
$parent = new TreeNode(1, 'parent');
58+
$child = new TreeNode(2, 'child');
59+
$replacement = new TreeNode(3, 'replacement');
60+
61+
$parent->setLeft($child);
62+
$child->replaceWith($replacement);
63+
64+
$this->assertSame($replacement, $parent->left);
65+
$this->assertSame($parent, $replacement->parent);
66+
}
67+
68+
public function testSetRightAndLeftTogether(): void
69+
{
70+
$parent = new TreeNode(1, 'parent');
71+
$leftChild = new TreeNode(2, 'left');
72+
$rightChild = new TreeNode(3, 'right');
73+
74+
$parent->setLeft($leftChild);
75+
$parent->setRight($rightChild);
76+
77+
$this->assertSame($leftChild, $parent->left);
78+
$this->assertSame($rightChild, $parent->right);
79+
$this->assertSame($parent, $leftChild->parent);
80+
$this->assertSame($parent, $rightChild->parent);
81+
}
82+
83+
public function testReplaceWithThrowsExceptionWhenNodeIsRoot(): void
84+
{
85+
$this->expectException(RuntimeException::class);
86+
$this->expectExceptionMessage('Cannot replace root node');
87+
88+
$root = new TreeNode(1, 'root');
89+
$replacement = new TreeNode(2, 'replacement');
90+
91+
$root->replaceWith($replacement);
92+
}
93+
94+
public function testRemoveFromParentRightChild(): void
95+
{
96+
$parent = new TreeNode(1, 'parent');
97+
$child = new TreeNode(2, 'child');
98+
99+
$parent->setRight($child);
100+
$child->removeFromParent();
101+
102+
$this->assertNull($parent->right);
103+
$this->assertNull($child->parent);
104+
}
105+
106+
public function testReplaceWithRightChild(): void
107+
{
108+
$parent = new TreeNode(1, 'parent');
109+
$child = new TreeNode(2, 'child');
110+
$replacement = new TreeNode(3, 'replacement');
111+
112+
$parent->setRight($child);
113+
$child->replaceWith($replacement);
114+
115+
$this->assertSame($replacement, $parent->right);
116+
$this->assertSame($parent, $replacement->parent);
117+
}
118+
}

0 commit comments

Comments
 (0)