Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
f8cc0bc
create helper classes
dimitrijjedich Dec 13, 2024
e56fd18
create Color enum
dimitrijjedich Dec 13, 2024
bfe4aec
add properties to new classes
dimitrijjedich Dec 13, 2024
d719f58
make Node properties nullable
dimitrijjedich Dec 13, 2024
29937d5
add constructors
dimitrijjedich Dec 13, 2024
71f4b43
move properties into constructor
dimitrijjedich Dec 13, 2024
e2fd9fe
formatting
dimitrijjedich Dec 13, 2024
0f6e991
add getter
dimitrijjedich Dec 13, 2024
79cfe32
implement setter for Color
dimitrijjedich Dec 13, 2024
4a63349
create insert method for most basic case
dimitrijjedich Dec 13, 2024
b6b2325
rename enums folder to Enum
dimitrijjedich Dec 13, 2024
8c05e0f
implement setter for right and left oif RedBlackNode
dimitrijjedich Dec 13, 2024
2fd5b24
add basic binary tree insertion to insert method of RedBlackTree
dimitrijjedich Dec 13, 2024
85b2e10
refactor insert
dimitrijjedich Dec 13, 2024
ae5032b
add step in insertion to have pointer on the new node
dimitrijjedich Dec 13, 2024
4c29df9
add uncle helper and getter for parent to RedBlackNode
dimitrijjedich Dec 13, 2024
0dbfdc3
add getter for parent and uncle helper to RedBlackNode
dimitrijjedich Dec 13, 2024
f574e77
add first case to insert
dimitrijjedich Dec 14, 2024
be785f2
add setter for parent
dimitrijjedich Dec 15, 2024
1e6df77
add rotateRight logic
dimitrijjedich Dec 15, 2024
1bc1ce8
formatting
dimitrijjedich Dec 15, 2024
d65e84c
fix default update of parent in rotateRight
dimitrijjedich Dec 15, 2024
6a11105
change surrounding if to guard pattern
dimitrijjedich Dec 15, 2024
8775e1d
extract parent handling and add update of parent of left node
dimitrijjedich Dec 15, 2024
0543755
cleanup null check
dimitrijjedich Dec 15, 2024
592ba6e
add rotateLeft logic
dimitrijjedich Dec 15, 2024
549a1b8
fix typo
dimitrijjedich Dec 15, 2024
c63d344
break insert after root creation
dimitrijjedich Dec 15, 2024
7727b29
add isLeftChild and isRightChild helper
dimitrijjedich Dec 15, 2024
09899e1
add sibling helper
dimitrijjedich Dec 15, 2024
05f33be
implement cases for black uncle
dimitrijjedich Dec 15, 2024
585f46d
add basic test setup
dimitrijjedich Dec 15, 2024
50358c8
fix sibling helper
dimitrijjedich Dec 15, 2024
8bb26d5
wip fix rotateLeft
dimitrijjedich Dec 15, 2024
e1d2701
add first tests
dimitrijjedich Dec 16, 2024
f48c354
fix test for rotateLeft
dimitrijjedich Dec 16, 2024
8d2d6a5
fix check for red uncle
dimitrijjedich Dec 16, 2024
03104d1
add fixRoot helper to RedBlackTree
dimitrijjedich Dec 16, 2024
bd15a3e
apply fixRoot after rotations
dimitrijjedich Dec 16, 2024
544921e
apply breakpoints to tree balancing loop
dimitrijjedich Dec 16, 2024
e5222ad
add basic insert test
dimitrijjedich Dec 16, 2024
d4b95f5
fix parent relation in rotations on root node
dimitrijjedich Dec 16, 2024
df5fcac
fix iteration of fixRoot
dimitrijjedich Dec 16, 2024
57bd015
add return type to fixRoot
dimitrijjedich Dec 16, 2024
59ca079
add tests for tree with three nodes
dimitrijjedich Dec 16, 2024
7679d04
add test helper
dimitrijjedich Dec 16, 2024
2be25d6
add test for negative numbers and large dataset
dimitrijjedich Dec 16, 2024
277a837
fix null pointer exception problem
dimitrijjedich Dec 16, 2024
5c4e608
fix condition for RL case
dimitrijjedich Dec 16, 2024
14298b6
reorganize condition to match parent, child structure
dimitrijjedich Dec 16, 2024
d4410a0
refactor fix of root into rotate methods
dimitrijjedich Dec 16, 2024
ecc447c
update tests to new syntax
dimitrijjedich Dec 16, 2024
546dd8c
add return type to setRoot
dimitrijjedich Dec 16, 2024
29d4b86
extract binaryTree insert into method to reduce cognitive complexity
dimitrijjedich Dec 16, 2024
411e9b8
refactor to comply with typing
dimitrijjedich Dec 16, 2024
0bfbd5f
add update of parent to setter for left and right
dimitrijjedich Dec 16, 2024
1b504ab
remove usage of setParent from binaryTreeInsert
dimitrijjedich Dec 16, 2024
e7ccb43
remove setParent from rotate methods
dimitrijjedich Dec 16, 2024
b12e357
replace conditions with isLeftChild
dimitrijjedich Dec 16, 2024
b91dbf3
abstract similar logic of rotate methods into updateParent helper
dimitrijjedich Dec 16, 2024
c41e729
update do-while condition to prevent potential null pointer exception
dimitrijjedich Dec 16, 2024
3fee965
shut up PHPStan
dimitrijjedich Dec 16, 2024
62cf51a
make $value readonly
dimitrijjedich Dec 16, 2024
cce1403
update rotate method to use setter instead of updating parent
dimitrijjedich Dec 16, 2024
7af88eb
update setter for left and right to handle root node
dimitrijjedich Dec 16, 2024
648e737
refactor updateParent
dimitrijjedich Dec 16, 2024
9200a1d
update PHPDoc of updateParent
dimitrijjedich Dec 16, 2024
e458186
use new functionality of left and right setter in rotate
dimitrijjedich Dec 16, 2024
b224b07
add direction enum
dimitrijjedich Dec 17, 2024
174e280
update Direction enum by operation method
dimitrijjedich Dec 17, 2024
7ec4deb
implement isChild helper
dimitrijjedich Dec 17, 2024
255ea46
refactor updateParent to Direction enum and isChild usage
dimitrijjedich Dec 17, 2024
ca043dd
refactor usage of isLeftChild and isRightChild to isChild
dimitrijjedich Dec 17, 2024
b85e646
remove ucfirst for usage of name
dimitrijjedich Dec 17, 2024
54524b4
extend Direction enum by opposite
dimitrijjedich Dec 17, 2024
536d15e
introduce generic rotate method
dimitrijjedich Dec 17, 2024
70ff857
refactor rotateRight and rotateLeft to new rotate helper
dimitrijjedich Dec 17, 2024
4c7b2b6
refactor uncle to use sibling helper
dimitrijjedich Dec 17, 2024
947b8d3
add and update PHPDocs
dimitrijjedich Dec 17, 2024
1063bad
change visibility of binaryTreeInsert to private
dimitrijjedich Dec 17, 2024
d78eafe
deletion base setup
dimitrijjedich Dec 17, 2024
415f7c1
add binaryTreeDeletion
dimitrijjedich Dec 17, 2024
091f255
add two child case to binaryTreeDelete
dimitrijjedich Dec 17, 2024
1a75d01
remove unneeded returns
dimitrijjedich Dec 17, 2024
8fee520
move root check up and introduce parentChildDirection
dimitrijjedich Dec 17, 2024
27c7d34
refactor all isChild checks to usage of $parentChildDirection
dimitrijjedich Dec 17, 2024
8eba6ec
fix two child case relation updates
dimitrijjedich Dec 18, 2024
d0fc7b8
add basic deletion testcases
dimitrijjedich Dec 18, 2024
1b15022
fix parentChildDirection definition
dimitrijjedich Dec 18, 2024
130900b
add first tests for one and two child cases
dimitrijjedich Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/helper/Enum/Color.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Src\helper\Enum;

enum Color
{
case Red;
case Black;
}
22 changes: 22 additions & 0 deletions src/helper/Enum/Direction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Src\helper\Enum;

enum Direction: string
{
case Left = 'left';
case Right = 'right';

public function operation(string $operationName): string
{
return $operationName . $this->name;
}

public function opposite(): Direction
{
return match ($this) {
Direction::Left => Direction::Right,
Direction::Right => Direction::Left,
};
}
}
153 changes: 153 additions & 0 deletions src/helper/RedBlackNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

namespace Src\helper;

use Src\helper\Enum\Color;
use Src\helper\Enum\Direction;

class RedBlackNode
{
/**
* @param int $value
* @param RedBlackNode|null $parent
* @param Color $color
* @param RedBlackNode|null $left
* @param RedBlackNode|null $right
*/
public function __construct(
private readonly int $value,
private ?RedBlackNode $parent = null,
private Color $color = Color::Red,
private ?RedBlackNode $left = null,
private ?RedBlackNode $right = null
) {}

public function getValue(): int
{
return $this->value;
}

public function getParent(): ?RedBlackNode
{
return $this->parent;
}

public function setParent(?RedBlackNode $node): void
{
$this->parent = $node;
}

public function getColor(): Color
{
return $this->color;
}

public function setColor(Color $color): void
{
$this->color = $color;
}

public function getLeft(): ?RedBlackNode
{
return $this->left;
}

public function setLeft(?RedBlackNode $node): void
{
$this->left = $node;
$node?->setParent($this);
}

public function getRight(): ?RedBlackNode
{
return $this->right;
}

public function setRight(?RedBlackNode $node): void
{
$this->right = $node;
$node?->setParent($this);
}

/**
* Check if $this node is the child of its parents in $direction
*
* @param Direction $direction
* @return bool
*/
public function isChild(Direction $direction): bool
{
if (!$this->parent) {
return false;
} elseif ($this->parent->{$direction->operation('get')}() === $this) {
return true;
}
return false;
}

/**
* Returns the sibling of the parent of $this
*
* @return RedBlackNode|null
*/
public function uncle(): ?RedBlackNode
{
if (!$this->parent || !$this->parent->getParent()) {
return null;
}
return $this->parent->sibling();
}

/**
* Return the opposite child of the parents to $this
*
* @return RedBlackNode|null
*/
public function sibling(): ?RedBlackNode
{
if (!$this->parent) {
return null;
}
return $this->isChild(Direction::Left)
? $this->parent->getRight()
: $this->parent->getLeft();
}

/**
* Rotate the subtree around $this in the $direction and updates relations
*
* @param RedBlackTree $tree
* @param Direction $direction
* @return void
*/
public function rotate(RedBlackTree $tree, Direction $direction): void
{
if (!$this->{$direction->opposite()->value}) {
return;
}
$grandchild = $this->{$direction->opposite()->value}->{$direction->operation('get')}();
$this->updateParent($tree, $direction->opposite());
$this->{$direction->opposite()->value}->{$direction->operation('set')}($this);
$this->{$direction->opposite()->operation('set')}($grandchild);
}

/**
* Update the parent of the node to the child in the $direction
* should $this be root (both children checks null), update parent to null and set child in $direction as new root
*
* @param RedBlackTree $tree
* @param Direction $direction
* @return void
*/
private function updateParent(RedBlackTree $tree, Direction $direction): void
{
if ($this->isChild(Direction::Left)) {
$this->parent->setLeft($this->{$direction->value});
} elseif ($this->isChild(Direction::Right)) {
$this->parent->setRight($this->{$direction->value});
} else {
$this->{$direction->value}->setParent(null);
$tree->setRoot($this->{$direction->value});
}
}
}
156 changes: 156 additions & 0 deletions src/helper/RedBlackTree.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<?php

namespace Src\helper;

use Src\helper\Enum\Color;
use Src\helper\Enum\Direction;

class RedBlackTree
{
/**
* @param RedBlackNode|null $root
*/
public function __construct(private ?RedBlackNode $root = null) {}

public function getRoot(): ?RedBlackNode
{
return $this->root;
}

public function setRoot(RedBlackNode $node): void
{
$this->root = $node;
}

/**
* Inserts a new node with provided $value into the tree
*
* @param int $value
* @return void
*/
public function insert(int $value): void
{
if ($this->root == null) {
$this->root = new RedBlackNode($value);
$this->root->setColor(Color::Black);
return;
}
$pointer = $this->binaryTreeInsert($this->root, $value);
do {
if ($pointer->getColor() == Color::Black) {
break;
}
if ($pointer->uncle() && $pointer->uncle()->getColor() == Color::Red) {
$pointer->getParent()->setColor(Color::Black);
$pointer->uncle()->setColor(Color::Black);
$pointer = $pointer->getParent()->getParent();
continue;
}
if ($pointer->getParent()->isChild(Direction::Left) && $pointer->isChild(Direction::Left)) {
$pointer->getParent()->getParent()->rotate($this, Direction::Right);
$pointer->getParent()->setColor(Color::Black);
$pointer->sibling()->setColor(Color::Red);
continue;
}
if ($pointer->getParent()->isChild(Direction::Left) && $pointer->isChild(Direction::Right)) {
$pointer->getParent()->rotate($this, Direction::Left);
$pointer->getParent()->rotate($this, Direction::Right);
$pointer->setColor(Color::Black);
$pointer->getRight()->setColor(Color::Red);
continue;
}
if ($pointer->getParent()->isChild(Direction::Right) && $pointer->isChild(Direction::Right)) {
$pointer->getParent()->getParent()->rotate($this, Direction::Left);
$pointer->getParent()->setColor(Color::Black);
$pointer->sibling()->setColor(Color::Red);
continue;
}
if ($pointer->getParent()->isChild(Direction::Right) && $pointer->isChild(Direction::Left)) {
$pointer->getParent()->rotate($this, Direction::Right);
$pointer->getParent()->rotate($this, Direction::Left);
$pointer->setColor(Color::Black);
$pointer->getLeft()->setColor(Color::Red);
$pointer->getParent()->setColor(Color::Black);
continue;
}
break;
} while ($pointer);
}

/**
* Delete the provided node from the tree
*
* @param RedBlackNode $node
* @return void
*/
public function delete(RedBlackNode $node): void
{
$this->binaryTreeDelete($node);
}

/**
* BinaryTree insert of $value starting at $pointer
*
* @param RedBlackNode $pointer
* @param int $value
* @return RedBlackNode
*/
private function binaryTreeInsert(RedBlackNode $pointer, int $value): RedBlackNode
{
$node = new RedBlackNode($value);
do {
if ($value < $pointer->getValue()) {
if ($pointer->getLeft() == null) {
$pointer->setLeft($node);
return $node;
}
$pointer = $pointer->getLeft();
} else {
if ($pointer->getRight() == null) {
$pointer->setRight($node);
return $node;
}
$pointer = $pointer->getRight();
}
} while (true);
}

/**
* BinaryTree deletion of $node
*
* @param RedBlackNode $node
* @return void
*/
private function binaryTreeDelete(RedBlackNode $node): void
{
if ($node === $this->root) {
$this->root = null;
return;
}
$parentChildDirection = $node->isChild(Direction::Left)
? Direction::Left
: Direction::Right;
if (!$node->getRight() && !$node->getLeft()) {
$node->getParent()->{$parentChildDirection->operation('set')}(null);
}
if ($node->getLeft() && $node->getRight()) {
$pointer = $node->getRight();
while ($pointer->getLeft()) {
$pointer = $pointer->getLeft();
}
if ($pointer->getParent()->isChild(Direction::Left)) {
$pointer->getParent()->setLeft($pointer->getRight());
} else {
$pointer->getParent()->setRight($pointer->getRight());
}
$node->getParent()->{$parentChildDirection->operation('set')}($pointer);
$pointer->setRight($node->getRight());
}
if ($node->getLeft() && !$node->getRight()) {
$node->getParent()->{$parentChildDirection->operation('set')}($node->getLeft());
}
if ($node->getRight() && !$node->getLeft()) {
$node->getParent()->{$parentChildDirection->operation('set')}($node->getRight());
}
}
}
Loading
Loading