From f8cc0bc0296bd2d5161aa55781194155b9599411 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 07:22:26 +0100 Subject: [PATCH 01/90] create helper classes --- src/helper/RedBlackNode.php | 7 +++++++ src/helper/RedBlackTree.php | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 src/helper/RedBlackNode.php create mode 100644 src/helper/RedBlackTree.php diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php new file mode 100644 index 0000000..0a3ca36 --- /dev/null +++ b/src/helper/RedBlackNode.php @@ -0,0 +1,7 @@ + Date: Fri, 13 Dec 2024 07:29:19 +0100 Subject: [PATCH 02/90] create Color enum --- src/helper/enums/Color.php | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/helper/enums/Color.php diff --git a/src/helper/enums/Color.php b/src/helper/enums/Color.php new file mode 100644 index 0000000..b0adc5d --- /dev/null +++ b/src/helper/enums/Color.php @@ -0,0 +1,9 @@ + Date: Fri, 13 Dec 2024 07:29:50 +0100 Subject: [PATCH 03/90] add properties to new classes --- src/helper/RedBlackNode.php | 6 ++++++ src/helper/RedBlackTree.php | 1 + 2 files changed, 7 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 0a3ca36..4663e08 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -2,6 +2,12 @@ namespace Src\helper; +use Src\helper\enums\Color; + class RedBlackNode { + private RedBlackNode $left; + private RedBlackNode $right; + private int $value; + private Color $color; } diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index b808f48..f5b044a 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -4,4 +4,5 @@ class RedBlackTree { + private RedBlackNode $root; } From d719f5857cbd8268dec41ade79839cfd7064dec3 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 07:34:09 +0100 Subject: [PATCH 04/90] make Node properties nullable --- src/helper/RedBlackNode.php | 4 ++-- src/helper/RedBlackTree.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 4663e08..f2711dc 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -6,8 +6,8 @@ class RedBlackNode { - private RedBlackNode $left; - private RedBlackNode $right; + private ?RedBlackNode $left; + private ?RedBlackNode $right; private int $value; private Color $color; } diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index f5b044a..49839bc 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -4,5 +4,5 @@ class RedBlackTree { - private RedBlackNode $root; + private ?RedBlackNode $root; } From 29937d5b4114829a8c2eda87e37f56035ab4ff27 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 07:34:34 +0100 Subject: [PATCH 05/90] add constructors --- src/helper/RedBlackNode.php | 7 +++++++ src/helper/RedBlackTree.php | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index f2711dc..c382831 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -10,4 +10,11 @@ class RedBlackNode private ?RedBlackNode $right; private int $value; private Color $color; + + public function __construct(int $value) { + $this->value = $value; + $this->color = Color::Red; + $this->left = null; + $this->right = null; + } } diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 49839bc..866f549 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -5,4 +5,8 @@ class RedBlackTree { private ?RedBlackNode $root; + + public function __construct() { + $this->root = null; + } } From 71f4b439748cb2244722abc7326faf8e00217539 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 07:37:26 +0100 Subject: [PATCH 06/90] move properties into constructor --- src/helper/RedBlackNode.php | 17 ++++++----------- src/helper/RedBlackTree.php | 6 +----- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index c382831..5ed98f1 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -6,15 +6,10 @@ class RedBlackNode { - private ?RedBlackNode $left; - private ?RedBlackNode $right; - private int $value; - private Color $color; - - public function __construct(int $value) { - $this->value = $value; - $this->color = Color::Red; - $this->left = null; - $this->right = null; - } + public function __construct( + private int $value, + private Color $color = Color::Red, + private ?RedBlackNode $left = null, + private ?RedBlackNode $right = null + ) {} } diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 866f549..81538d6 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -4,9 +4,5 @@ class RedBlackTree { - private ?RedBlackNode $root; - - public function __construct() { - $this->root = null; - } + public function __construct(private ?RedBlackNode $root = null) {} } From e2fd9fea7cf1e43ba0fa59a23a8f5d2c8dfb8b01 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 07:39:56 +0100 Subject: [PATCH 07/90] formatting --- src/helper/RedBlackNode.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 5ed98f1..56a155e 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -7,8 +7,8 @@ class RedBlackNode { public function __construct( - private int $value, - private Color $color = Color::Red, + private int $value, + private Color $color = Color::Red, private ?RedBlackNode $left = null, private ?RedBlackNode $right = null ) {} From 0f6e991353a0dc7e6370887f8383543f086e4202 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 07:40:09 +0100 Subject: [PATCH 08/90] add getter --- src/helper/RedBlackNode.php | 20 ++++++++++++++++++++ src/helper/RedBlackTree.php | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 56a155e..ef60a38 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -12,4 +12,24 @@ public function __construct( private ?RedBlackNode $left = null, private ?RedBlackNode $right = null ) {} + + public function getValue(): int + { + return $this->value; + } + + public function getColor(): Color + { + return $this->color; + } + + public function getLeft(): ?RedBlackNode + { + return $this->left; + } + + public function getRight(): ?RedBlackNode + { + return $this->right; + } } diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 81538d6..b801d3f 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -5,4 +5,9 @@ class RedBlackTree { public function __construct(private ?RedBlackNode $root = null) {} + + public function getRoot(): ?RedBlackNode + { + return $this->root; + } } From 79cfe323e1fd8e2a7b34a372469264b125405480 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 07:45:04 +0100 Subject: [PATCH 09/90] implement setter for Color --- src/helper/RedBlackNode.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index ef60a38..7c6c3d5 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -23,6 +23,11 @@ public function getColor(): Color return $this->color; } + public function setColor(Color $color): void + { + $this->color = $color; + } + public function getLeft(): ?RedBlackNode { return $this->left; From 4a633491143ca129de94a25db467895796e611ed Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 07:45:30 +0100 Subject: [PATCH 10/90] create insert method for most basic case --- src/helper/RedBlackTree.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index b801d3f..8715283 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -2,6 +2,8 @@ namespace Src\helper; +use Src\helper\enums\Color; + class RedBlackTree { public function __construct(private ?RedBlackNode $root = null) {} @@ -10,4 +12,12 @@ public function getRoot(): ?RedBlackNode { return $this->root; } + + public function insert(RedBlackNode $node): void + { + if ($this->root == null) { + $this->root = $node; + $node->setColor(Color::Black); + } + } } From b6b23254875d84279c51520f87f2d29723c98616 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 19:06:54 +0100 Subject: [PATCH 11/90] rename enums folder to Enum --- src/helper/{enums => Enum}/Color.php | 2 +- src/helper/RedBlackNode.php | 2 +- src/helper/RedBlackTree.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/helper/{enums => Enum}/Color.php (65%) diff --git a/src/helper/enums/Color.php b/src/helper/Enum/Color.php similarity index 65% rename from src/helper/enums/Color.php rename to src/helper/Enum/Color.php index b0adc5d..64f5f2e 100644 --- a/src/helper/enums/Color.php +++ b/src/helper/Enum/Color.php @@ -1,6 +1,6 @@ Date: Fri, 13 Dec 2024 19:33:23 +0100 Subject: [PATCH 12/90] implement setter for right and left oif RedBlackNode --- src/helper/RedBlackNode.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index f139889..b196b86 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -33,8 +33,18 @@ public function getLeft(): ?RedBlackNode return $this->left; } + public function setLeft(RedBlackNode $node): void + { + $this->left = $node; + } + public function getRight(): ?RedBlackNode { return $this->right; } + + public function setRight(RedBlackNode $node): void + { + $this->right = $node; + } } From 2fd5b249f5bba3b79737da9373070edaeb516766 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 19:36:06 +0100 Subject: [PATCH 13/90] add basic binary tree insertion to insert method of RedBlackTree --- src/helper/RedBlackTree.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index c973913..279aef5 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -19,5 +19,21 @@ public function insert(RedBlackNode $node): void $this->root = $node; $node->setColor(Color::Black); } + $pointer = $this->root; + do { + if ($node->getValue() < $pointer->getValue()) { + if ($pointer->getLeft() == null) { + $pointer->setLeft($node); + break; + } + $pointer = $pointer->getLeft(); + } else { + if ($pointer->getRight() == null) { + $pointer->setRight($node); + break; + } + $pointer = $pointer->getLRight(); + } + } while (true); } } From 85b2e10faf535224a06ec3bc0389df84dd5565a2 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 23:03:10 +0100 Subject: [PATCH 14/90] refactor insert now accepts int as input and handles parent assignment add parent property to RedBlackNode with default null to RedBlackNode --- src/helper/RedBlackNode.php | 1 + src/helper/RedBlackTree.php | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index b196b86..4cb47a2 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -8,6 +8,7 @@ class RedBlackNode { public function __construct( private int $value, + private ?RedBlackNode $parent = null, private Color $color = Color::Red, private ?RedBlackNode $left = null, private ?RedBlackNode $right = null diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 279aef5..ad03b6a 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -13,23 +13,23 @@ public function getRoot(): ?RedBlackNode return $this->root; } - public function insert(RedBlackNode $node): void + public function insert(int $value): void { if ($this->root == null) { - $this->root = $node; - $node->setColor(Color::Black); + $this->root = new RedBlackNode($value); + $this->root->setColor(Color::Black); } $pointer = $this->root; do { - if ($node->getValue() < $pointer->getValue()) { + if ($value < $pointer->getValue()) { if ($pointer->getLeft() == null) { - $pointer->setLeft($node); + $pointer->setLeft(new RedBlackNode($value, $pointer)); break; } $pointer = $pointer->getLeft(); } else { if ($pointer->getRight() == null) { - $pointer->setRight($node); + $pointer->setRight(new RedBlackNode($value, $pointer)); break; } $pointer = $pointer->getLRight(); From ae5032bfa27194a88b93f903f7d7fad369acce1b Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Fri, 13 Dec 2024 23:05:43 +0100 Subject: [PATCH 15/90] add step in insertion to have pointer on the new node --- src/helper/RedBlackTree.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index ad03b6a..b530f70 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -24,12 +24,14 @@ public function insert(int $value): void if ($value < $pointer->getValue()) { if ($pointer->getLeft() == null) { $pointer->setLeft(new RedBlackNode($value, $pointer)); + $pointer = $pointer->getLeft(); break; } $pointer = $pointer->getLeft(); } else { if ($pointer->getRight() == null) { $pointer->setRight(new RedBlackNode($value, $pointer)); + $pointer = $pointer->getRight(); break; } $pointer = $pointer->getLRight(); From 4c29df96ca13cdae66395ff6f50fb617a1ca8709 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sat, 14 Dec 2024 00:52:42 +0100 Subject: [PATCH 16/90] add uncle helper and getter for parent to RedBlackNode --- src/helper/RedBlackNode.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 4cb47a2..50fc5a4 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -19,6 +19,10 @@ public function getValue(): int return $this->value; } + public function getParent(): ?RedBlackNode { + return $this->parent; + } + public function getColor(): Color { return $this->color; @@ -48,4 +52,14 @@ public function setRight(RedBlackNode $node): void { $this->right = $node; } + + public function uncle(): ?RedBlackNode + { + if ($this->parent == null || $this->parent->getParent() == null) { + return null; + } + return $this->parent === $this->parent->getParent()->getLeft() + ? $this->parent->getParent()->getRight() + : $this->parent->getParent()->getLeft(); + } } From 0dbfdc3ad4be52ea9567e631c4c512895c036620 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sat, 14 Dec 2024 00:53:19 +0100 Subject: [PATCH 17/90] add getter for parent and uncle helper to RedBlackNode --- src/helper/RedBlackNode.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 50fc5a4..796c6a5 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -19,7 +19,8 @@ public function getValue(): int return $this->value; } - public function getParent(): ?RedBlackNode { + public function getParent(): ?RedBlackNode + { return $this->parent; } @@ -55,7 +56,7 @@ public function setRight(RedBlackNode $node): void public function uncle(): ?RedBlackNode { - if ($this->parent == null || $this->parent->getParent() == null) { + if (!$this->parent || !$this->parent->getParent()) { return null; } return $this->parent === $this->parent->getParent()->getLeft() From f574e77557f6ccb5d8d3803250ccff45e6e38d58 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sat, 14 Dec 2024 01:07:14 +0100 Subject: [PATCH 18/90] add first case to insert --- src/helper/RedBlackTree.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index b530f70..c0cab43 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -37,5 +37,16 @@ public function insert(int $value): void $pointer = $pointer->getLRight(); } } while (true); + 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; + } + } while (true); } } From be785f20318e527a676f818f54092f49bf381d6a Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 13:26:46 +0100 Subject: [PATCH 19/90] add setter for parent --- src/helper/RedBlackNode.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 796c6a5..c44f9d5 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -24,6 +24,11 @@ public function getParent(): ?RedBlackNode return $this->parent; } + public function setParent(RedBlackNode $node): void + { + $this->parent = $node; + } + public function getColor(): Color { return $this->color; From 1e6df773c56bff578a67408097e9d15d76d2dc55 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 13:26:57 +0100 Subject: [PATCH 20/90] add rotateRight logic --- src/helper/RedBlackNode.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index c44f9d5..65687b5 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -68,4 +68,25 @@ public function uncle(): ?RedBlackNode ? $this->parent->getParent()->getRight() : $this->parent->getParent()->getLeft(); } + + public function rotateRight(): void + { + if($this->left) { + if ($this->parent) { + if ($this->parent->getLeft() === $this) { + $this->parent->setLeft($this->left); + } + $this->parent->setRight($this->left); + } + $this->parent = $this->left; + $leftRightGrandchild = $this->parent->getRight(); + $this->parent->setRight($this); + if($leftRightGrandchild) { + $this->left = $leftRightGrandchild; + $leftRightGrandchild->setParent($this); + } else { + $this->left = null; + } + } + } } From 1bc1ce860e09801c1009b93b6f08991d4f8a7414 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 13:28:27 +0100 Subject: [PATCH 21/90] formatting --- src/helper/RedBlackNode.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 65687b5..7a0b8d7 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -71,7 +71,7 @@ public function uncle(): ?RedBlackNode public function rotateRight(): void { - if($this->left) { + if ($this->left) { if ($this->parent) { if ($this->parent->getLeft() === $this) { $this->parent->setLeft($this->left); @@ -81,7 +81,7 @@ public function rotateRight(): void $this->parent = $this->left; $leftRightGrandchild = $this->parent->getRight(); $this->parent->setRight($this); - if($leftRightGrandchild) { + if ($leftRightGrandchild) { $this->left = $leftRightGrandchild; $leftRightGrandchild->setParent($this); } else { From d65e84c2918dc5b36308967cf946377a708d3c59 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 13:28:46 +0100 Subject: [PATCH 22/90] fix default update of parent in rotateRight --- src/helper/RedBlackNode.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 7a0b8d7..e1b3dec 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -75,8 +75,9 @@ public function rotateRight(): void if ($this->parent) { if ($this->parent->getLeft() === $this) { $this->parent->setLeft($this->left); + } else { + $this->parent->setRight($this->left); } - $this->parent->setRight($this->left); } $this->parent = $this->left; $leftRightGrandchild = $this->parent->getRight(); From 6a111056bd0b9cf5ce9592e8d114ddee73e925a6 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 13:30:24 +0100 Subject: [PATCH 23/90] change surrounding if to guard pattern --- src/helper/RedBlackNode.php | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index e1b3dec..644c337 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -71,23 +71,24 @@ public function uncle(): ?RedBlackNode public function rotateRight(): void { - if ($this->left) { - if ($this->parent) { - if ($this->parent->getLeft() === $this) { - $this->parent->setLeft($this->left); - } else { - $this->parent->setRight($this->left); - } - } - $this->parent = $this->left; - $leftRightGrandchild = $this->parent->getRight(); - $this->parent->setRight($this); - if ($leftRightGrandchild) { - $this->left = $leftRightGrandchild; - $leftRightGrandchild->setParent($this); + if (!$this->left) { + return; + } + if ($this->parent) { + if ($this->parent->getLeft() === $this) { + $this->parent->setLeft($this->left); } else { - $this->left = null; + $this->parent->setRight($this->left); } } + $this->parent = $this->left; + $leftRightGrandchild = $this->parent->getRight(); + $this->parent->setRight($this); + if ($leftRightGrandchild) { + $this->left = $leftRightGrandchild; + $leftRightGrandchild->setParent($this); + } else { + $this->left = null; + } } } From 8775e1d216aedfeb5f48ff442bcb02dc7dec41df Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 13:47:54 +0100 Subject: [PATCH 24/90] extract parent handling and add update of parent of left node --- src/helper/RedBlackNode.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 644c337..7c8a99f 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -74,12 +74,14 @@ public function rotateRight(): void if (!$this->left) { return; } - if ($this->parent) { - if ($this->parent->getLeft() === $this) { - $this->parent->setLeft($this->left); + $parent = $this->parent; + if ($parent) { + if ($parent->getLeft() === $this) { + $parent->setLeft($this->left); } else { - $this->parent->setRight($this->left); + $parent->setRight($this->left); } + $this->left->setParent($parent); } $this->parent = $this->left; $leftRightGrandchild = $this->parent->getRight(); From 05437552e5b5b89bb5facc9bb805e091806122b0 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 13:59:21 +0100 Subject: [PATCH 25/90] cleanup null check --- src/helper/RedBlackNode.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 7c8a99f..0a719e0 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -86,11 +86,7 @@ public function rotateRight(): void $this->parent = $this->left; $leftRightGrandchild = $this->parent->getRight(); $this->parent->setRight($this); - if ($leftRightGrandchild) { - $this->left = $leftRightGrandchild; - $leftRightGrandchild->setParent($this); - } else { - $this->left = null; - } + $this->left = $leftRightGrandchild; + $leftRightGrandchild?->setParent($this); } } From 592ba6e4b377f39ccb93e7ac12ffa0295e762bac Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 14:03:39 +0100 Subject: [PATCH 26/90] add rotateLeft logic --- src/helper/RedBlackNode.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 0a719e0..9acdaba 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -89,4 +89,25 @@ public function rotateRight(): void $this->left = $leftRightGrandchild; $leftRightGrandchild?->setParent($this); } + + public function rotateLeft(): void + { + if (!$this->right) { + return; + } + $parent = $this->parent; + if ($parent) { + if ($parent->getLeft() === $this) { + $parent->setLeft($this->right); + } else { + $parent->setRight($this->right); + } + $this->right->setParent($parent); + } + $this->parent = $this->right; + $rightLeftGrandchild = $this->parent->getLeft(); + $this->parent->setRight($this); + $this->right = $rightLeftGrandchild; + $rightLeftGrandchild?->setParent($this); + } } From 549a1b8eb8f049274d0f7f1ad33f0f34da277229 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 17:29:12 +0100 Subject: [PATCH 27/90] fix typo --- src/helper/RedBlackTree.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index c0cab43..9ca5b64 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -34,7 +34,7 @@ public function insert(int $value): void $pointer = $pointer->getRight(); break; } - $pointer = $pointer->getLRight(); + $pointer = $pointer->getRight(); } } while (true); do { From c63d344171c1ac2f97506c699a46663ceb6f9592 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 17:31:37 +0100 Subject: [PATCH 28/90] break insert after root creation --- src/helper/RedBlackTree.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 9ca5b64..059b53d 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -18,6 +18,7 @@ public function insert(int $value): void if ($this->root == null) { $this->root = new RedBlackNode($value); $this->root->setColor(Color::Black); + return; } $pointer = $this->root; do { From 7727b294eeb639e3bc7493b32076ac771aff80e7 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 17:42:23 +0100 Subject: [PATCH 29/90] add isLeftChild and isRightChild helper --- src/helper/RedBlackNode.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 9acdaba..8473446 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -59,6 +59,22 @@ public function setRight(RedBlackNode $node): void $this->right = $node; } + public function isLeftChild(): bool + { + if ($this->parent && $this->parent->getLeft() === $this) { + return true; + } + return false; + } + + public function isRightChild(): bool + { + if ($this->parent && $this->parent->getRight() === $this) { + return true; + } + return false; + } + public function uncle(): ?RedBlackNode { if (!$this->parent || !$this->parent->getParent()) { From 09899e1376d94cfeafa9e4bba3efca1b78c1c1ca Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 17:43:06 +0100 Subject: [PATCH 30/90] add sibling helper --- src/helper/RedBlackNode.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 8473446..3be0612 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -85,6 +85,16 @@ public function uncle(): ?RedBlackNode : $this->parent->getParent()->getLeft(); } + public function sibling(): ?RedBlackNode + { + if (!$this->parent) { + return null; + } + return $this === $this->parent->getLeft() + ? $this->parent->getParent()->getRight() + : $this->parent->getParent()->getLeft(); + } + public function rotateRight(): void { if (!$this->left) { From 05f33beefe44f69a1fef9b1bdfbe505426f192a7 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 20:03:13 +0100 Subject: [PATCH 31/90] implement cases for black uncle --- src/helper/RedBlackTree.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 059b53d..d3f7c5e 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -48,6 +48,29 @@ public function insert(int $value): void $pointer = $pointer->getParent()->getParent(); continue; } + if ($pointer->isLeftChild() && $pointer->getParent()->isLeftChild()) { + $pointer->getParent()->getParent()->rotateRight(); + $pointer->getParent()->setColor(Color::Black); + $pointer->sibling()->setColor(Color::Red); + } + if ($pointer->getParent()->isLeftChild() && $pointer->isRightChild()) { + $pointer->getParent()->rotateLeft(); + $pointer->getParent()->rotateRight(); + $pointer->setColor(Color::Black); + $pointer->getRight()->setColor(Color::Red); + } + if ($pointer->isRightChild() && $pointer->getParent()->isRightChild()) { + $pointer->getParent()->getParent()->rotateLeft(); + $pointer->getParent()->setColor(Color::Black); + $pointer->sibling()->setColor(Color::Red); + } + if ($pointer->getParent()->isRightChild() && $pointer->isRightChild()) { + $pointer->getParent()->rotateRight(); + $pointer->getParent()->rotateLeft(); + $pointer->setColor(Color::Black); + $pointer->getLeft()->setColor(Color::Red); + $pointer->getParent()->setColor(Color::Black); + } } while (true); } } From 585f46dcd25743e0a7fa8dc82bf3e21e81c3d9bd Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 20:20:48 +0100 Subject: [PATCH 32/90] add basic test setup --- tests/RedBlackTreeTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/RedBlackTreeTest.php diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php new file mode 100644 index 0000000..f2d8955 --- /dev/null +++ b/tests/RedBlackTreeTest.php @@ -0,0 +1,10 @@ + Date: Sun, 15 Dec 2024 20:41:33 +0100 Subject: [PATCH 33/90] fix sibling helper --- src/helper/RedBlackNode.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 3be0612..ca43a9f 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -91,8 +91,8 @@ public function sibling(): ?RedBlackNode return null; } return $this === $this->parent->getLeft() - ? $this->parent->getParent()->getRight() - : $this->parent->getParent()->getLeft(); + ? $this->parent->getRight() + : $this->parent->getLeft(); } public function rotateRight(): void From 8bb26d5071ce4225093f3bd36629ba5f4f61380f Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Sun, 15 Dec 2024 22:22:27 +0100 Subject: [PATCH 34/90] wip fix rotateLeft --- src/helper/RedBlackNode.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index ca43a9f..dd51f14 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -132,7 +132,7 @@ public function rotateLeft(): void } $this->parent = $this->right; $rightLeftGrandchild = $this->parent->getLeft(); - $this->parent->setRight($this); + $this->parent->setLeft($this); $this->right = $rightLeftGrandchild; $rightLeftGrandchild?->setParent($this); } From e1d2701f2a35434f0d5dcf3398382a266db68662 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 08:06:33 +0100 Subject: [PATCH 35/90] add first tests --- tests/RedBlackTreeTest.php | 130 +++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index f2d8955..6434b7a 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -3,8 +3,138 @@ declare(strict_types=1); use PHPUnit\Framework\TestCase; +use Src\helper\Enum\Color; +use Src\helper\RedBlackNode; final class RedBlackTreeTest extends TestCase { + public function testNodeGettersAndSetters(): void + { + $parent = new RedBlackNode(10); + $left = new RedBlackNode(5); + $right = new RedBlackNode(15); + $node = new RedBlackNode(20, $parent); + $node->setLeft($left); + $node->setRight($right); + + $this->assertEquals(20, $node->getValue()); + $this->assertSame($parent, $node->getParent()); + $this->assertSame($left, $node->getLeft()); + $this->assertSame($right, $node->getRight()); + + $node->setColor(Color::Black); + $this->assertEquals(Color::Black, $node->getColor()); + + $node->setParent($left); + $this->assertSame($left, $node->getParent()); + } + + + public function testIsLeftChild(): void + { + $parent = new RedBlackNode(10); + $child = new RedBlackNode(5, $parent); + $parent->setLeft($child); + + $this->assertTrue($child->isLeftChild()); + } + + public function testIsRightChild(): void + { + $parent = new RedBlackNode(10); + $child = new RedBlackNode(15, $parent); + $parent->setRight($child); + + $this->assertTrue($child->isRightChild()); + } + + public function testUncle(): void + { + $grandparent = new RedBlackNode(20); + $parent = new RedBlackNode(10, $grandparent); + $uncle = new RedBlackNode(30, $grandparent); + + $grandparent->setLeft($parent); + $grandparent->setRight($uncle); + + $child = new RedBlackNode(5, $parent); + $parent->setLeft($child); + + $this->assertSame($uncle, $child->uncle()); + } + + public function testSibling(): void + { + $parent = new RedBlackNode(20); + $left = new RedBlackNode(10, $parent); + $right = new RedBlackNode(30, $parent); + $parent->setLeft($left); + $parent->setRight($right); + + $this->assertEquals($right, $left->sibling()); + $this->assertEquals($left, $right->sibling()); + } + + public function testRotateRight(): void + { + $grandparent = new RedBlackNode(10); + $node = new RedBlackNode(5, $grandparent); + $grandparent->setLeft($node); + + $nodeChildLeft = new RedBlackNode(3, $node); + $node->setLeft($nodeChildLeft); + $nodeChildRight = new RedBlackNode(7, $node); + $node->setRight($nodeChildRight); + + $leftGrandChild = new RedBlackNode(1, $nodeChildLeft); + $nodeChildLeft->setLeft($leftGrandChild); + $rightGrandChild = new RedBlackNode(4, $nodeChildLeft); + $nodeChildLeft->setRight($rightGrandChild); + + $node->rotateRight(); + + $this->assertSame($grandparent->getLeft(), $nodeChildLeft); + $this->assertSame($nodeChildLeft->getParent(), $grandparent); + + $this->assertSame($node->getParent(), $nodeChildLeft); + $this->assertSame($nodeChildLeft->getRight(), $node); + + $this->assertSame($node->getLeft(), $rightGrandChild); + $this->assertSame($rightGrandChild->getParent(), $node); + + $this->assertSame($node->getRight(), $nodeChildRight); + $this->assertSame($nodeChildRight->getParent(), $node); + } + + public function testRotateLeft(): void + { + $grandparent = new RedBlackNode(10); + $node = new RedBlackNode(5, $grandparent); + $grandparent->setLeft($node); + + $nodeChildLeft = new RedBlackNode(3, $node); + $node->setLeft($nodeChildLeft); + $nodeChildRight = new RedBlackNode(7, $node); + $node->setRight($nodeChildRight); + + $leftGrandChild = new RedBlackNode(6, $nodeChildRight); + $nodeChildRight->setLeft($leftGrandChild); + $rightGrandChild = new RedBlackNode(8, $nodeChildRight); + $nodeChildLeft->setRight($rightGrandChild); + + $node->rotateLeft(); + + $this->assertSame($grandparent->getLeft(), $nodeChildRight); + $this->assertSame($nodeChildRight->getParent(), $grandparent); + + $this->assertSame($nodeChildRight->getLeft(), $node); + $this->assertSame($node->getParent(), $nodeChildRight); + + $this->assertSame($node->getRight(), $leftGrandChild); + $this->assertSame($leftGrandChild->getParent(), $node); + + $this->assertSame($nodeChildRight->getRight(), $rightGrandChild); + $this->assertSame($nodeChildLeft->getParent(), $nodeChildRight); + } } From f48c3546828a84aef446d7ead000b29bdbd59cfb Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 21:12:25 +0100 Subject: [PATCH 36/90] fix test for rotateLeft --- tests/RedBlackTreeTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index 6434b7a..2a16596 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -121,7 +121,7 @@ public function testRotateLeft(): void $leftGrandChild = new RedBlackNode(6, $nodeChildRight); $nodeChildRight->setLeft($leftGrandChild); $rightGrandChild = new RedBlackNode(8, $nodeChildRight); - $nodeChildLeft->setRight($rightGrandChild); + $nodeChildRight->setRight($rightGrandChild); $node->rotateLeft(); @@ -135,6 +135,6 @@ public function testRotateLeft(): void $this->assertSame($leftGrandChild->getParent(), $node); $this->assertSame($nodeChildRight->getRight(), $rightGrandChild); - $this->assertSame($nodeChildLeft->getParent(), $nodeChildRight); + $this->assertSame($rightGrandChild->getParent(), $nodeChildRight); } } From 8d2d6a5ebc21a3afe03d4bae8c575554d0345479 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 21:30:08 +0100 Subject: [PATCH 37/90] fix check for red uncle --- src/helper/RedBlackTree.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index d3f7c5e..bef5a26 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -42,7 +42,7 @@ public function insert(int $value): void if ($pointer->getColor() == Color::Black) { break; } - if (!$pointer->uncle() && $pointer->uncle()->getColor() == Color::Red) { + if ($pointer->uncle() && $pointer->uncle()->getColor() == Color::Red) { $pointer->getParent()->setColor(Color::Black); $pointer->uncle()->setColor(Color::Black); $pointer = $pointer->getParent()->getParent(); From 03104d1babee97273bcec38635f1deca8ff4f7b6 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 21:30:43 +0100 Subject: [PATCH 38/90] add fixRoot helper to RedBlackTree --- src/helper/RedBlackTree.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index bef5a26..fdc446d 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -73,4 +73,15 @@ public function insert(int $value): void } } while (true); } + + private function fixRoot(RedBlackNode $node) + { + $pointer = $node; + while ($node->getParent() != null) { + $pointer = $node->getParent(); + } + if ($pointer !== $this->root) { + $this->root = $pointer; + } + } } From bd15a3ed4d98c4704bc26a21f953980041545ae7 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 21:31:10 +0100 Subject: [PATCH 39/90] apply fixRoot after rotations --- src/helper/RedBlackTree.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index fdc446d..0b9bb57 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -50,23 +50,27 @@ public function insert(int $value): void } if ($pointer->isLeftChild() && $pointer->getParent()->isLeftChild()) { $pointer->getParent()->getParent()->rotateRight(); + $this->fixRoot($pointer); $pointer->getParent()->setColor(Color::Black); $pointer->sibling()->setColor(Color::Red); } if ($pointer->getParent()->isLeftChild() && $pointer->isRightChild()) { $pointer->getParent()->rotateLeft(); $pointer->getParent()->rotateRight(); + $this->fixRoot($pointer); $pointer->setColor(Color::Black); $pointer->getRight()->setColor(Color::Red); } if ($pointer->isRightChild() && $pointer->getParent()->isRightChild()) { $pointer->getParent()->getParent()->rotateLeft(); + $this->fixRoot($pointer); $pointer->getParent()->setColor(Color::Black); $pointer->sibling()->setColor(Color::Red); } if ($pointer->getParent()->isRightChild() && $pointer->isRightChild()) { $pointer->getParent()->rotateRight(); $pointer->getParent()->rotateLeft(); + $this->fixRoot($pointer); $pointer->setColor(Color::Black); $pointer->getLeft()->setColor(Color::Red); $pointer->getParent()->setColor(Color::Black); From 544921e3507eb03df8956bde2dd933767687cf29 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 21:31:42 +0100 Subject: [PATCH 40/90] apply breakpoints to tree balancing loop --- src/helper/RedBlackTree.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 0b9bb57..25ea2b3 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -53,6 +53,7 @@ public function insert(int $value): void $this->fixRoot($pointer); $pointer->getParent()->setColor(Color::Black); $pointer->sibling()->setColor(Color::Red); + continue; } if ($pointer->getParent()->isLeftChild() && $pointer->isRightChild()) { $pointer->getParent()->rotateLeft(); @@ -60,12 +61,14 @@ public function insert(int $value): void $this->fixRoot($pointer); $pointer->setColor(Color::Black); $pointer->getRight()->setColor(Color::Red); + continue; } if ($pointer->isRightChild() && $pointer->getParent()->isRightChild()) { $pointer->getParent()->getParent()->rotateLeft(); $this->fixRoot($pointer); $pointer->getParent()->setColor(Color::Black); $pointer->sibling()->setColor(Color::Red); + continue; } if ($pointer->getParent()->isRightChild() && $pointer->isRightChild()) { $pointer->getParent()->rotateRight(); @@ -74,7 +77,9 @@ public function insert(int $value): void $pointer->setColor(Color::Black); $pointer->getLeft()->setColor(Color::Red); $pointer->getParent()->setColor(Color::Black); + continue; } + break; } while (true); } From e5222ad349e2faea3675bc51f00cf83b4c446e5e Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 21:31:57 +0100 Subject: [PATCH 41/90] add basic insert test --- tests/RedBlackTreeTest.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index 2a16596..06e14aa 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -5,9 +5,36 @@ use PHPUnit\Framework\TestCase; use Src\helper\Enum\Color; use Src\helper\RedBlackNode; +use Src\helper\RedBlackTree; final class RedBlackTreeTest extends TestCase { + public function testInsert(): void + { + $tree = new RedBlackTree(); + + $tree->insert(10); + $root = $tree->getRoot(); + + $this->assertNotNull($root); + $this->assertEquals(10, $root->getValue()); + $this->assertEquals(Color::Black, $root->getColor()); // Root must always be black + + $tree->insert(5); + $left = $root->getLeft(); + + $this->assertNotNull($left); + $this->assertEquals(5, $left->getValue()); + $this->assertEquals(Color::Red, $left->getColor()); + + $tree->insert(15); + $right = $root->getRight(); + + $this->assertNotNull($right); + $this->assertEquals(15, $right->getValue()); + $this->assertEquals(Color::Red, $right->getColor()); + } + public function testNodeGettersAndSetters(): void { $parent = new RedBlackNode(10); From d4b95f55bf6c55c90493e69c89071aa293afcfc0 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 22:04:24 +0100 Subject: [PATCH 42/90] fix parent relation in rotations on root node --- src/helper/RedBlackNode.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index dd51f14..1afa65e 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -24,7 +24,7 @@ public function getParent(): ?RedBlackNode return $this->parent; } - public function setParent(RedBlackNode $node): void + public function setParent(?RedBlackNode $node): void { $this->parent = $node; } @@ -108,6 +108,8 @@ public function rotateRight(): void $parent->setRight($this->left); } $this->left->setParent($parent); + } else { + $this->left->setParent(null); } $this->parent = $this->left; $leftRightGrandchild = $this->parent->getRight(); @@ -129,6 +131,8 @@ public function rotateLeft(): void $parent->setRight($this->right); } $this->right->setParent($parent); + } else { + $this->right->setParent(null); } $this->parent = $this->right; $rightLeftGrandchild = $this->parent->getLeft(); From df5fcac13e2e64500e10adbfd288f2233ef84b3b Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 22:05:06 +0100 Subject: [PATCH 43/90] fix iteration of fixRoot --- src/helper/RedBlackTree.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 25ea2b3..162852d 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -86,8 +86,8 @@ public function insert(int $value): void private function fixRoot(RedBlackNode $node) { $pointer = $node; - while ($node->getParent() != null) { - $pointer = $node->getParent(); + while ($pointer->getParent() != null) { + $pointer = $pointer->getParent(); } if ($pointer !== $this->root) { $this->root = $pointer; From 57bd015670840f09e8b630f7078cc78a7aab6bb5 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 22:05:19 +0100 Subject: [PATCH 44/90] add return type to fixRoot --- src/helper/RedBlackTree.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 162852d..14be170 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -83,7 +83,7 @@ public function insert(int $value): void } while (true); } - private function fixRoot(RedBlackNode $node) + private function fixRoot(RedBlackNode $node): void { $pointer = $node; while ($pointer->getParent() != null) { From 59ca079a809fcac28cabfd610da674c10fb4c362 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 22:06:26 +0100 Subject: [PATCH 45/90] add tests for tree with three nodes --- tests/RedBlackTreeTest.php | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index 06e14aa..bc9be58 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -35,6 +35,52 @@ public function testInsert(): void $this->assertEquals(Color::Red, $right->getColor()); } + public function testInsertToTheRightTriggersRebalancing(): void + { + $tree = new RedBlackTree(); + + $tree->insert(10); + $tree->insert(20); + $tree->insert(30); + + $root = $tree->getRoot(); + + $this->assertEquals(20, $root->getValue()); + $this->assertEquals(Color::Black, $root->getColor()); + + $left = $root->getLeft(); + $right = $root->getRight(); + + $this->assertEquals(10, $left->getValue()); + $this->assertEquals(Color::Red, $left->getColor()); + + $this->assertEquals(30, $right->getValue()); + $this->assertEquals(Color::Red, $right->getColor()); + } + + public function testInsertToTheLeftTriggersRebalancing(): void + { + $tree = new RedBlackTree(); + + $tree->insert(30); + $tree->insert(20); + $tree->insert(10); + + $root = $tree->getRoot(); + + $this->assertEquals(20, $root->getValue()); + $this->assertEquals(Color::Black, $root->getColor()); + + $left = $root->getLeft(); + $right = $root->getRight(); + + $this->assertEquals(10, $left->getValue()); + $this->assertEquals(Color::Red, $left->getColor()); + + $this->assertEquals(30, $right->getValue()); + $this->assertEquals(Color::Red, $right->getColor()); + } + public function testNodeGettersAndSetters(): void { $parent = new RedBlackNode(10); From 7679d0451a8994ebe140b4547d3c4e2b01364474 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 22:08:48 +0100 Subject: [PATCH 46/90] add test helper --- tests/RedBlackTreeTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index bc9be58..1b8f0a9 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -210,4 +210,21 @@ public function testRotateLeft(): void $this->assertSame($nodeChildRight->getRight(), $rightGrandChild); $this->assertSame($rightGrandChild->getParent(), $nodeChildRight); } + + private function isBalancedRedBlackTree(?RedBlackNode $node): bool + { + if ($node === null) { + return true; + } + + $left = $node->getLeft(); + $right = $node->getRight(); + + if ($node->getColor() === Color::Red) { + $this->assertEquals(Color::Black, $left?->getColor()); + $this->assertEquals(Color::Black, $right?->getColor()); + } + + return $this->isBalancedRedBlackTree($left) && $this->isBalancedRedBlackTree($right); + } } From 2be25d60b549974d7ba42d5d1b9f5e0c5a8db2c2 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 22:13:44 +0100 Subject: [PATCH 47/90] add test for negative numbers and large dataset --- tests/RedBlackTreeTest.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index 1b8f0a9..e11a95b 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -81,6 +81,36 @@ public function testInsertToTheLeftTriggersRebalancing(): void $this->assertEquals(Color::Red, $right->getColor()); } + public function testInsertHandlesNegativeValues(): void + { + $tree = new RedBlackTree(); + + $tree->insert(-10); + $tree->insert(-20); + $tree->insert(-5); + + $root = $tree->getRoot(); + + $this->assertEquals(-10, $root->getValue()); + $this->assertEquals(-20, $root->getLeft()->getValue()); + $this->assertEquals(-5, $root->getRight()->getValue()); + } + + public function testInsertHandlesLargeNumberOfValues(): void + { + $tree = new RedBlackTree(); + $values = range(1, 1000); + + foreach ($values as $value) { + $tree->insert($value); + } + + $root = $tree->getRoot(); + + $this->assertNotNull($root); + $this->assertTrue($this->isBalancedRedBlackTree($root)); + } + public function testNodeGettersAndSetters(): void { $parent = new RedBlackNode(10); From 277a837a5c8bddb33ba1234f534f19b209a29167 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 22:14:04 +0100 Subject: [PATCH 48/90] fix null pointer exception problem --- tests/RedBlackTreeTest.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index e11a95b..3e1a3d9 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -251,8 +251,12 @@ private function isBalancedRedBlackTree(?RedBlackNode $node): bool $right = $node->getRight(); if ($node->getColor() === Color::Red) { - $this->assertEquals(Color::Black, $left?->getColor()); - $this->assertEquals(Color::Black, $right?->getColor()); + if ($left) { + $this->assertEquals(Color::Black, $left->getColor()); + } + if ($right) { + $this->assertEquals(Color::Black, $right?->getColor()); + } } return $this->isBalancedRedBlackTree($left) && $this->isBalancedRedBlackTree($right); From 5c4e608673d35bbcb23cdde3fe973302fa7bb8ee Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 22:58:40 +0100 Subject: [PATCH 49/90] fix condition for RL case --- src/helper/RedBlackTree.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 14be170..21b905d 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -48,7 +48,7 @@ public function insert(int $value): void $pointer = $pointer->getParent()->getParent(); continue; } - if ($pointer->isLeftChild() && $pointer->getParent()->isLeftChild()) { + if ($pointer->getParent()->isLeftChild() && $pointer->isLeftChild()) { $pointer->getParent()->getParent()->rotateRight(); $this->fixRoot($pointer); $pointer->getParent()->setColor(Color::Black); @@ -70,7 +70,7 @@ public function insert(int $value): void $pointer->sibling()->setColor(Color::Red); continue; } - if ($pointer->getParent()->isRightChild() && $pointer->isRightChild()) { + if ($pointer->getParent()->isRightChild() && $pointer->isLeftChild()) { $pointer->getParent()->rotateRight(); $pointer->getParent()->rotateLeft(); $this->fixRoot($pointer); From 14298b6677ecf63d702adb8724b54dcff0fd217c Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 22:59:37 +0100 Subject: [PATCH 50/90] reorganize condition to match parent, child structure --- src/helper/RedBlackTree.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 21b905d..5227951 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -63,7 +63,7 @@ public function insert(int $value): void $pointer->getRight()->setColor(Color::Red); continue; } - if ($pointer->isRightChild() && $pointer->getParent()->isRightChild()) { + if ($pointer->getParent()->isRightChild() && $pointer->isRightChild()) { $pointer->getParent()->getParent()->rotateLeft(); $this->fixRoot($pointer); $pointer->getParent()->setColor(Color::Black); From d4410a0c9afc996442f8a674c6d01b9856c58e87 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 23:06:36 +0100 Subject: [PATCH 51/90] refactor fix of root into rotate methods --- src/helper/RedBlackNode.php | 6 ++++-- src/helper/RedBlackTree.php | 32 +++++++++++--------------------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 1afa65e..b43efd2 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -95,7 +95,7 @@ public function sibling(): ?RedBlackNode : $this->parent->getLeft(); } - public function rotateRight(): void + public function rotateRight(RedBlackTree $tree): void { if (!$this->left) { return; @@ -110,6 +110,7 @@ public function rotateRight(): void $this->left->setParent($parent); } else { $this->left->setParent(null); + $tree->setRoot($this->left); } $this->parent = $this->left; $leftRightGrandchild = $this->parent->getRight(); @@ -118,7 +119,7 @@ public function rotateRight(): void $leftRightGrandchild?->setParent($this); } - public function rotateLeft(): void + public function rotateLeft(RedBlackTree $tree): void { if (!$this->right) { return; @@ -133,6 +134,7 @@ public function rotateLeft(): void $this->right->setParent($parent); } else { $this->right->setParent(null); + $tree->setRoot($this->right); } $this->parent = $this->right; $rightLeftGrandchild = $this->parent->getLeft(); diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 5227951..5356317 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -13,6 +13,11 @@ public function getRoot(): ?RedBlackNode return $this->root; } + public function setRoot(RedBlackNode $node) + { + $this->root = $node; + } + public function insert(int $value): void { if ($this->root == null) { @@ -49,31 +54,27 @@ public function insert(int $value): void continue; } if ($pointer->getParent()->isLeftChild() && $pointer->isLeftChild()) { - $pointer->getParent()->getParent()->rotateRight(); - $this->fixRoot($pointer); + $pointer->getParent()->getParent()->rotateRight($this); $pointer->getParent()->setColor(Color::Black); $pointer->sibling()->setColor(Color::Red); continue; } if ($pointer->getParent()->isLeftChild() && $pointer->isRightChild()) { - $pointer->getParent()->rotateLeft(); - $pointer->getParent()->rotateRight(); - $this->fixRoot($pointer); + $pointer->getParent()->rotateLeft($this); + $pointer->getParent()->rotateRight($this); $pointer->setColor(Color::Black); $pointer->getRight()->setColor(Color::Red); continue; } if ($pointer->getParent()->isRightChild() && $pointer->isRightChild()) { - $pointer->getParent()->getParent()->rotateLeft(); - $this->fixRoot($pointer); + $pointer->getParent()->getParent()->rotateLeft($this); $pointer->getParent()->setColor(Color::Black); $pointer->sibling()->setColor(Color::Red); continue; } if ($pointer->getParent()->isRightChild() && $pointer->isLeftChild()) { - $pointer->getParent()->rotateRight(); - $pointer->getParent()->rotateLeft(); - $this->fixRoot($pointer); + $pointer->getParent()->rotateRight($this); + $pointer->getParent()->rotateLeft($this); $pointer->setColor(Color::Black); $pointer->getLeft()->setColor(Color::Red); $pointer->getParent()->setColor(Color::Black); @@ -82,15 +83,4 @@ public function insert(int $value): void break; } while (true); } - - private function fixRoot(RedBlackNode $node): void - { - $pointer = $node; - while ($pointer->getParent() != null) { - $pointer = $pointer->getParent(); - } - if ($pointer !== $this->root) { - $this->root = $pointer; - } - } } From ecc447c46b7e3394169cc2c94dc79bc24675d0b1 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 23:06:52 +0100 Subject: [PATCH 52/90] update tests to new syntax --- tests/RedBlackTreeTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index 3e1a3d9..028e056 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -182,6 +182,7 @@ public function testSibling(): void public function testRotateRight(): void { $grandparent = new RedBlackNode(10); + $redBlackTree = new RedBlackTree($grandparent); $node = new RedBlackNode(5, $grandparent); $grandparent->setLeft($node); @@ -195,7 +196,7 @@ public function testRotateRight(): void $rightGrandChild = new RedBlackNode(4, $nodeChildLeft); $nodeChildLeft->setRight($rightGrandChild); - $node->rotateRight(); + $node->rotateRight($redBlackTree); $this->assertSame($grandparent->getLeft(), $nodeChildLeft); $this->assertSame($nodeChildLeft->getParent(), $grandparent); @@ -213,6 +214,7 @@ public function testRotateRight(): void public function testRotateLeft(): void { $grandparent = new RedBlackNode(10); + $redBlackTree = new RedBlackTree($grandparent); $node = new RedBlackNode(5, $grandparent); $grandparent->setLeft($node); @@ -226,7 +228,7 @@ public function testRotateLeft(): void $rightGrandChild = new RedBlackNode(8, $nodeChildRight); $nodeChildRight->setRight($rightGrandChild); - $node->rotateLeft(); + $node->rotateLeft($redBlackTree); $this->assertSame($grandparent->getLeft(), $nodeChildRight); $this->assertSame($nodeChildRight->getParent(), $grandparent); From 546dd8c52550c1628f6e6f45363bf42014378783 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 23:07:42 +0100 Subject: [PATCH 53/90] add return type to setRoot --- src/helper/RedBlackTree.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 5356317..21d225e 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -13,7 +13,7 @@ public function getRoot(): ?RedBlackNode return $this->root; } - public function setRoot(RedBlackNode $node) + public function setRoot(RedBlackNode $node): void { $this->root = $node; } From 29d4b86522a550d2ef67e287e399ffcc1c018972 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 23:14:11 +0100 Subject: [PATCH 54/90] extract binaryTree insert into method to reduce cognitive complexity --- src/helper/RedBlackTree.php | 45 ++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 21d225e..7b5b0b7 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -25,24 +25,7 @@ public function insert(int $value): void $this->root->setColor(Color::Black); return; } - $pointer = $this->root; - do { - if ($value < $pointer->getValue()) { - if ($pointer->getLeft() == null) { - $pointer->setLeft(new RedBlackNode($value, $pointer)); - $pointer = $pointer->getLeft(); - break; - } - $pointer = $pointer->getLeft(); - } else { - if ($pointer->getRight() == null) { - $pointer->setRight(new RedBlackNode($value, $pointer)); - $pointer = $pointer->getRight(); - break; - } - $pointer = $pointer->getRight(); - } - } while (true); + $pointer = $this->binaryTreeInsert($this->root, $value); do { if ($pointer->getColor() == Color::Black) { break; @@ -83,4 +66,30 @@ public function insert(int $value): void break; } while (true); } + + /** + * BinaryTree insert of $value starting at $pointer + * + * @param RedBlackNode $pointer + * @param int $value + * @return RedBlackNode + */ + public function binaryTreeInsert(RedBlackNode $pointer, int $value): RedBlackNode + { + do { + if ($value < $pointer->getValue()) { + if ($pointer->getLeft() == null) { + $pointer->setLeft(new RedBlackNode($value, $pointer)); + return $pointer->getLeft(); + } + $pointer = $pointer->getLeft(); + } else { + if ($pointer->getRight() == null) { + $pointer->setRight(new RedBlackNode($value, $pointer)); + return $pointer->getRight(); + } + $pointer = $pointer->getRight(); + } + } while (true); + } } From 411e9b8a7c15c59b28566241274e742ba5ff0639 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 23:16:57 +0100 Subject: [PATCH 55/90] refactor to comply with typing --- src/helper/RedBlackTree.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 7b5b0b7..aa66bd5 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -76,17 +76,20 @@ public function insert(int $value): void */ public function binaryTreeInsert(RedBlackNode $pointer, int $value): RedBlackNode { + $node = new RedBlackNode($value); do { if ($value < $pointer->getValue()) { if ($pointer->getLeft() == null) { - $pointer->setLeft(new RedBlackNode($value, $pointer)); - return $pointer->getLeft(); + $pointer->setLeft($node); + $node->setParent($pointer); + return $node; } $pointer = $pointer->getLeft(); } else { if ($pointer->getRight() == null) { - $pointer->setRight(new RedBlackNode($value, $pointer)); - return $pointer->getRight(); + $pointer->setRight($node); + $node->setParent($pointer); + return $node; } $pointer = $pointer->getRight(); } From 0bfbd5f58b8aa8fcb04bc3c5fe9815823ea36466 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 23:30:15 +0100 Subject: [PATCH 56/90] add update of parent to setter for left and right --- src/helper/RedBlackNode.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index b43efd2..2a06768 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -47,6 +47,7 @@ public function getLeft(): ?RedBlackNode public function setLeft(RedBlackNode $node): void { $this->left = $node; + $node->setParent($this); } public function getRight(): ?RedBlackNode @@ -57,6 +58,7 @@ public function getRight(): ?RedBlackNode public function setRight(RedBlackNode $node): void { $this->right = $node; + $node->setParent($this); } public function isLeftChild(): bool From 1b504ab4447b13309b204811e049aa88bb74d7f1 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 23:32:05 +0100 Subject: [PATCH 57/90] remove usage of setParent from binaryTreeInsert --- src/helper/RedBlackTree.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index aa66bd5..99ed7aa 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -81,14 +81,12 @@ public function binaryTreeInsert(RedBlackNode $pointer, int $value): RedBlackNod if ($value < $pointer->getValue()) { if ($pointer->getLeft() == null) { $pointer->setLeft($node); - $node->setParent($pointer); return $node; } $pointer = $pointer->getLeft(); } else { if ($pointer->getRight() == null) { $pointer->setRight($node); - $node->setParent($pointer); return $node; } $pointer = $pointer->getRight(); From e7ccb432fd4c0b43e5718ff8a118b0a49a11b2c0 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 23:32:48 +0100 Subject: [PATCH 58/90] remove setParent from rotate methods --- src/helper/RedBlackNode.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 2a06768..96fdad3 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -109,7 +109,6 @@ public function rotateRight(RedBlackTree $tree): void } else { $parent->setRight($this->left); } - $this->left->setParent($parent); } else { $this->left->setParent(null); $tree->setRoot($this->left); @@ -133,7 +132,6 @@ public function rotateLeft(RedBlackTree $tree): void } else { $parent->setRight($this->right); } - $this->right->setParent($parent); } else { $this->right->setParent(null); $tree->setRoot($this->right); From b12e357574b2911957cf20dfc4288a2e5f39c163 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Mon, 16 Dec 2024 23:39:16 +0100 Subject: [PATCH 59/90] replace conditions with isLeftChild --- src/helper/RedBlackNode.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 96fdad3..3a9dca4 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -82,7 +82,7 @@ public function uncle(): ?RedBlackNode if (!$this->parent || !$this->parent->getParent()) { return null; } - return $this->parent === $this->parent->getParent()->getLeft() + return $this->parent->isLeftChild() ? $this->parent->getParent()->getRight() : $this->parent->getParent()->getLeft(); } @@ -92,7 +92,7 @@ public function sibling(): ?RedBlackNode if (!$this->parent) { return null; } - return $this === $this->parent->getLeft() + return $this->isLeftChild() ? $this->parent->getRight() : $this->parent->getLeft(); } @@ -104,7 +104,7 @@ public function rotateRight(RedBlackTree $tree): void } $parent = $this->parent; if ($parent) { - if ($parent->getLeft() === $this) { + if ($this->isLeftChild()) { $parent->setLeft($this->left); } else { $parent->setRight($this->left); @@ -127,7 +127,7 @@ public function rotateLeft(RedBlackTree $tree): void } $parent = $this->parent; if ($parent) { - if ($parent->getLeft() === $this) { + if ($this->isLeftChild()) { $parent->setLeft($this->right); } else { $parent->setRight($this->right); From b91dbf3062c9d3ea962022bf9741c2602976205f Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 00:14:23 +0100 Subject: [PATCH 60/90] abstract similar logic of rotate methods into updateParent helper --- src/helper/RedBlackNode.php | 43 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 3a9dca4..73a8fa8 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -102,17 +102,7 @@ public function rotateRight(RedBlackTree $tree): void if (!$this->left) { return; } - $parent = $this->parent; - if ($parent) { - if ($this->isLeftChild()) { - $parent->setLeft($this->left); - } else { - $parent->setRight($this->left); - } - } else { - $this->left->setParent(null); - $tree->setRoot($this->left); - } + $this->updateParent($tree, 'left'); $this->parent = $this->left; $leftRightGrandchild = $this->parent->getRight(); $this->parent->setRight($this); @@ -125,21 +115,34 @@ public function rotateLeft(RedBlackTree $tree): void if (!$this->right) { return; } + $this->updateParent($tree, 'right'); + $this->parent = $this->right; + $rightLeftGrandchild = $this->parent->getLeft(); + $this->parent->setLeft($this); + $this->right = $rightLeftGrandchild; + $rightLeftGrandchild?->setParent($this); + } + + /** + * Update the parent of the node to the child in the $direction + * should $this be the root, update parent to null and set the child in $direction as new root + * + * @param RedBlackTree $tree + * @param string $direction left|right + * @return void + */ + private function updateParent(RedBlackTree $tree, string $direction): void + { $parent = $this->parent; if ($parent) { if ($this->isLeftChild()) { - $parent->setLeft($this->right); + $parent->setLeft($this->$direction); } else { - $parent->setRight($this->right); + $parent->setRight($this->$direction); } } else { - $this->right->setParent(null); - $tree->setRoot($this->right); + $this->$direction->setParent(null); + $tree->setRoot($this->$direction); } - $this->parent = $this->right; - $rightLeftGrandchild = $this->parent->getLeft(); - $this->parent->setLeft($this); - $this->right = $rightLeftGrandchild; - $rightLeftGrandchild?->setParent($this); } } From c41e7293be6adacb4a1796d04426842d9521f9da Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 00:19:26 +0100 Subject: [PATCH 61/90] update do-while condition to prevent potential null pointer exception --- src/helper/RedBlackTree.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 99ed7aa..a60379d 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -64,7 +64,7 @@ public function insert(int $value): void continue; } break; - } while (true); + } while ($pointer); } /** From 3fee9650a29f9fd86bcf2340283463052077a615 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 00:26:46 +0100 Subject: [PATCH 62/90] shut up PHPStan the problem arises, because the parent is set to left (null save) first and then the right is retrieved from the new parent. The previously unknown (not null save) parent is now null save but not recognised by PHPStan The same goes for the rotate left in the other direction --- src/helper/RedBlackNode.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 73a8fa8..29b631a 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -102,9 +102,9 @@ public function rotateRight(RedBlackTree $tree): void if (!$this->left) { return; } + $leftRightGrandchild = $this->left->getRight(); $this->updateParent($tree, 'left'); $this->parent = $this->left; - $leftRightGrandchild = $this->parent->getRight(); $this->parent->setRight($this); $this->left = $leftRightGrandchild; $leftRightGrandchild?->setParent($this); @@ -115,9 +115,9 @@ public function rotateLeft(RedBlackTree $tree): void if (!$this->right) { return; } + $rightLeftGrandchild = $this->right->getLeft(); $this->updateParent($tree, 'right'); $this->parent = $this->right; - $rightLeftGrandchild = $this->parent->getLeft(); $this->parent->setLeft($this); $this->right = $rightLeftGrandchild; $rightLeftGrandchild?->setParent($this); From 62cf51a677ba5a73555487ecb0a2ef0c75245fbc Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 00:36:16 +0100 Subject: [PATCH 63/90] make $value readonly --- src/helper/RedBlackNode.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 29b631a..687dec2 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -7,7 +7,7 @@ class RedBlackNode { public function __construct( - private int $value, + private readonly int $value, private ?RedBlackNode $parent = null, private Color $color = Color::Red, private ?RedBlackNode $left = null, From cce1403f73c451572364b92aae1b01119b41a429 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 00:44:00 +0100 Subject: [PATCH 64/90] update rotate method to use setter instead of updating parent --- src/helper/RedBlackNode.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 687dec2..5aad95d 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -104,8 +104,7 @@ public function rotateRight(RedBlackTree $tree): void } $leftRightGrandchild = $this->left->getRight(); $this->updateParent($tree, 'left'); - $this->parent = $this->left; - $this->parent->setRight($this); + $this->left->setRight($this); $this->left = $leftRightGrandchild; $leftRightGrandchild?->setParent($this); } @@ -117,8 +116,7 @@ public function rotateLeft(RedBlackTree $tree): void } $rightLeftGrandchild = $this->right->getLeft(); $this->updateParent($tree, 'right'); - $this->parent = $this->right; - $this->parent->setLeft($this); + $this->right->setLeft($this); $this->right = $rightLeftGrandchild; $rightLeftGrandchild?->setParent($this); } From 7af88ebe437afb64a8bf98483ebc733fab61ff7d Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 00:44:36 +0100 Subject: [PATCH 65/90] update setter for left and right to handle root node --- src/helper/RedBlackNode.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 5aad95d..0c22777 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -44,10 +44,10 @@ public function getLeft(): ?RedBlackNode return $this->left; } - public function setLeft(RedBlackNode $node): void + public function setLeft(?RedBlackNode $node): void { $this->left = $node; - $node->setParent($this); + $node?->setParent($this); } public function getRight(): ?RedBlackNode @@ -55,10 +55,10 @@ public function getRight(): ?RedBlackNode return $this->right; } - public function setRight(RedBlackNode $node): void + public function setRight(?RedBlackNode $node): void { $this->right = $node; - $node->setParent($this); + $node?->setParent($this); } public function isLeftChild(): bool From 648e737fd92532f56b481332eeb44b853f8af44b Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 00:48:31 +0100 Subject: [PATCH 66/90] refactor updateParent --- src/helper/RedBlackNode.php | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 0c22777..1af081d 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -131,13 +131,10 @@ public function rotateLeft(RedBlackTree $tree): void */ private function updateParent(RedBlackTree $tree, string $direction): void { - $parent = $this->parent; - if ($parent) { - if ($this->isLeftChild()) { - $parent->setLeft($this->$direction); - } else { - $parent->setRight($this->$direction); - } + if ($this->isLeftChild()) { + $this->parent->setLeft($this->$direction); + } elseif ($this->isRightChild()) { + $this->parent->setRight($this->$direction); } else { $this->$direction->setParent(null); $tree->setRoot($this->$direction); From 9200a1d8889d98ed10ac79096ba66c8600c66569 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 00:48:40 +0100 Subject: [PATCH 67/90] update PHPDoc of updateParent --- src/helper/RedBlackNode.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 1af081d..bc7febb 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -123,7 +123,7 @@ public function rotateLeft(RedBlackTree $tree): void /** * Update the parent of the node to the child in the $direction - * should $this be the root, update parent to null and set the child in $direction as new root + * should $this be root (both child check null), update parent to null and set child in $direction as new root * * @param RedBlackTree $tree * @param string $direction left|right From e458186e90b2a932492f6fd1cfc8c79f345cc58a Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 00:50:26 +0100 Subject: [PATCH 68/90] use new functionality of left and right setter in rotate --- src/helper/RedBlackNode.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index bc7febb..76b1550 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -105,8 +105,7 @@ public function rotateRight(RedBlackTree $tree): void $leftRightGrandchild = $this->left->getRight(); $this->updateParent($tree, 'left'); $this->left->setRight($this); - $this->left = $leftRightGrandchild; - $leftRightGrandchild?->setParent($this); + $this->setLeft($leftRightGrandchild); } public function rotateLeft(RedBlackTree $tree): void @@ -117,8 +116,7 @@ public function rotateLeft(RedBlackTree $tree): void $rightLeftGrandchild = $this->right->getLeft(); $this->updateParent($tree, 'right'); $this->right->setLeft($this); - $this->right = $rightLeftGrandchild; - $rightLeftGrandchild?->setParent($this); + $this->setRight($rightLeftGrandchild); } /** From b224b074e3e277fdb3e3cae2ea4038adcd36ce96 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 02:22:32 +0100 Subject: [PATCH 69/90] add direction enum --- src/helper/Enum/Direction.php | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/helper/Enum/Direction.php diff --git a/src/helper/Enum/Direction.php b/src/helper/Enum/Direction.php new file mode 100644 index 0000000..78e8860 --- /dev/null +++ b/src/helper/Enum/Direction.php @@ -0,0 +1,9 @@ + Date: Tue, 17 Dec 2024 02:29:49 +0100 Subject: [PATCH 70/90] update Direction enum by operation method --- src/helper/Enum/Direction.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helper/Enum/Direction.php b/src/helper/Enum/Direction.php index 78e8860..341c775 100644 --- a/src/helper/Enum/Direction.php +++ b/src/helper/Enum/Direction.php @@ -6,4 +6,9 @@ enum Direction: string { case Left = 'left'; case Right = 'right'; + + public function operation(string $operationName): string + { + return $operationName . ucfirst($this->value); + } } From 7ec4deb948dd180c08bad18d5fbc6cef05384eab Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 02:30:31 +0100 Subject: [PATCH 71/90] implement isChild helper --- src/helper/RedBlackNode.php | 11 +++++++++++ tests/RedBlackTreeTest.php | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 76b1550..6c1ddb5 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -3,6 +3,7 @@ namespace Src\helper; use Src\helper\Enum\Color; +use Src\helper\Enum\Direction; class RedBlackNode { @@ -61,6 +62,16 @@ public function setRight(?RedBlackNode $node): void $node?->setParent($this); } + public function isChild(Direction $direction): bool + { + if (!$this->parent) { + return false; + } elseif ($this->parent->{$direction->operation('get')}() === $this) { + return true; + } + return false; + } + public function isLeftChild(): bool { if ($this->parent && $this->parent->getLeft() === $this) { diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index 028e056..eb7666e 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase; use Src\helper\Enum\Color; +use Src\helper\Enum\Direction; use Src\helper\RedBlackNode; use Src\helper\RedBlackTree; @@ -133,6 +134,18 @@ public function testNodeGettersAndSetters(): void $this->assertSame($left, $node->getParent()); } + public function testIsChild(): void + { + $parent = new RedBlackNode(10); + $child = new RedBlackNode(5, $parent); + $parent->setLeft($child); + $this->assertTrue($child->isChild(Direction::Left)); + + $parent = new RedBlackNode(10); + $child = new RedBlackNode(15, $parent); + $parent->setRight($child); + $this->assertTrue($child->isChild(Direction::Right)); + } public function testIsLeftChild(): void { From 255ea46c426d1cbd76690c5d9bf25ef52dedef74 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 02:33:02 +0100 Subject: [PATCH 72/90] refactor updateParent to Direction enum and isChild usage --- src/helper/RedBlackNode.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 6c1ddb5..6f36230 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -114,7 +114,7 @@ public function rotateRight(RedBlackTree $tree): void return; } $leftRightGrandchild = $this->left->getRight(); - $this->updateParent($tree, 'left'); + $this->updateParent($tree, Direction::Left); $this->left->setRight($this); $this->setLeft($leftRightGrandchild); } @@ -125,7 +125,7 @@ public function rotateLeft(RedBlackTree $tree): void return; } $rightLeftGrandchild = $this->right->getLeft(); - $this->updateParent($tree, 'right'); + $this->updateParent($tree, Direction::Right); $this->right->setLeft($this); $this->setRight($rightLeftGrandchild); } @@ -135,18 +135,18 @@ public function rotateLeft(RedBlackTree $tree): void * should $this be root (both child check null), update parent to null and set child in $direction as new root * * @param RedBlackTree $tree - * @param string $direction left|right + * @param Direction $direction * @return void */ - private function updateParent(RedBlackTree $tree, string $direction): void + private function updateParent(RedBlackTree $tree, Direction $direction): void { - if ($this->isLeftChild()) { - $this->parent->setLeft($this->$direction); - } elseif ($this->isRightChild()) { - $this->parent->setRight($this->$direction); + 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->setParent(null); - $tree->setRoot($this->$direction); + $this->{$direction->value}->setParent(null); + $tree->setRoot($this->{$direction->value}); } } } From ca043dd57533f2f8211abcffcd90a97a62d64f53 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 02:34:01 +0100 Subject: [PATCH 73/90] refactor usage of isLeftChild and isRightChild to isChild --- src/helper/RedBlackNode.php | 20 ++------------------ src/helper/RedBlackTree.php | 9 +++++---- tests/RedBlackTreeTest.php | 18 ------------------ 3 files changed, 7 insertions(+), 40 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 6f36230..d751ba4 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -72,28 +72,12 @@ public function isChild(Direction $direction): bool return false; } - public function isLeftChild(): bool - { - if ($this->parent && $this->parent->getLeft() === $this) { - return true; - } - return false; - } - - public function isRightChild(): bool - { - if ($this->parent && $this->parent->getRight() === $this) { - return true; - } - return false; - } - public function uncle(): ?RedBlackNode { if (!$this->parent || !$this->parent->getParent()) { return null; } - return $this->parent->isLeftChild() + return $this->parent->isChild(Direction::Left) ? $this->parent->getParent()->getRight() : $this->parent->getParent()->getLeft(); } @@ -103,7 +87,7 @@ public function sibling(): ?RedBlackNode if (!$this->parent) { return null; } - return $this->isLeftChild() + return $this->isChild(Direction::Left) ? $this->parent->getRight() : $this->parent->getLeft(); } diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index a60379d..06d8dba 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -3,6 +3,7 @@ namespace Src\helper; use Src\helper\Enum\Color; +use Src\helper\Enum\Direction; class RedBlackTree { @@ -36,26 +37,26 @@ public function insert(int $value): void $pointer = $pointer->getParent()->getParent(); continue; } - if ($pointer->getParent()->isLeftChild() && $pointer->isLeftChild()) { + if ($pointer->getParent()->isChild(Direction::Left) && $pointer->isChild(Direction::Left)) { $pointer->getParent()->getParent()->rotateRight($this); $pointer->getParent()->setColor(Color::Black); $pointer->sibling()->setColor(Color::Red); continue; } - if ($pointer->getParent()->isLeftChild() && $pointer->isRightChild()) { + if ($pointer->getParent()->isChild(Direction::Left) && $pointer->isChild(Direction::Right)) { $pointer->getParent()->rotateLeft($this); $pointer->getParent()->rotateRight($this); $pointer->setColor(Color::Black); $pointer->getRight()->setColor(Color::Red); continue; } - if ($pointer->getParent()->isRightChild() && $pointer->isRightChild()) { + if ($pointer->getParent()->isChild(Direction::Right) && $pointer->isChild(Direction::Right)) { $pointer->getParent()->getParent()->rotateLeft($this); $pointer->getParent()->setColor(Color::Black); $pointer->sibling()->setColor(Color::Red); continue; } - if ($pointer->getParent()->isRightChild() && $pointer->isLeftChild()) { + if ($pointer->getParent()->isChild(Direction::Right) && $pointer->isChild(Direction::Left)) { $pointer->getParent()->rotateRight($this); $pointer->getParent()->rotateLeft($this); $pointer->setColor(Color::Black); diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index eb7666e..2ee95e9 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -147,24 +147,6 @@ public function testIsChild(): void $this->assertTrue($child->isChild(Direction::Right)); } - public function testIsLeftChild(): void - { - $parent = new RedBlackNode(10); - $child = new RedBlackNode(5, $parent); - $parent->setLeft($child); - - $this->assertTrue($child->isLeftChild()); - } - - public function testIsRightChild(): void - { - $parent = new RedBlackNode(10); - $child = new RedBlackNode(15, $parent); - $parent->setRight($child); - - $this->assertTrue($child->isRightChild()); - } - public function testUncle(): void { $grandparent = new RedBlackNode(20); From b85e646edd622ffd14963a46ce5d79aa4cd0ce70 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 02:36:32 +0100 Subject: [PATCH 74/90] remove ucfirst for usage of name --- src/helper/Enum/Direction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/Enum/Direction.php b/src/helper/Enum/Direction.php index 341c775..0865510 100644 --- a/src/helper/Enum/Direction.php +++ b/src/helper/Enum/Direction.php @@ -9,6 +9,6 @@ enum Direction: string public function operation(string $operationName): string { - return $operationName . ucfirst($this->value); + return $operationName . $this->name; } } From 54524b4f7007178e4b5855207c6ed0678a206003 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 02:38:52 +0100 Subject: [PATCH 75/90] extend Direction enum by opposite --- src/helper/Enum/Direction.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/helper/Enum/Direction.php b/src/helper/Enum/Direction.php index 0865510..084b10c 100644 --- a/src/helper/Enum/Direction.php +++ b/src/helper/Enum/Direction.php @@ -11,4 +11,12 @@ 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, + }; + } } From 536d15e9f843164e0eb2dce2de2d1af7782d8b20 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 02:47:30 +0100 Subject: [PATCH 76/90] introduce generic rotate method --- src/helper/RedBlackNode.php | 11 +++++++ tests/RedBlackTreeTest.php | 64 +++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index d751ba4..9a32d98 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -92,6 +92,17 @@ public function sibling(): ?RedBlackNode : $this->parent->getLeft(); } + 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); + } + public function rotateRight(RedBlackTree $tree): void { if (!$this->left) { diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index 2ee95e9..86ff01f 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -206,6 +206,38 @@ public function testRotateRight(): void $this->assertSame($nodeChildRight->getParent(), $node); } + public function testRotateWithRight(): void + { + $grandparent = new RedBlackNode(10); + $redBlackTree = new RedBlackTree($grandparent); + $node = new RedBlackNode(5, $grandparent); + $grandparent->setLeft($node); + + $nodeChildLeft = new RedBlackNode(3, $node); + $node->setLeft($nodeChildLeft); + $nodeChildRight = new RedBlackNode(7, $node); + $node->setRight($nodeChildRight); + + $leftGrandChild = new RedBlackNode(1, $nodeChildLeft); + $nodeChildLeft->setLeft($leftGrandChild); + $rightGrandChild = new RedBlackNode(4, $nodeChildLeft); + $nodeChildLeft->setRight($rightGrandChild); + + $node->rotate($redBlackTree, Direction::Right); + + $this->assertSame($grandparent->getLeft(), $nodeChildLeft); + $this->assertSame($nodeChildLeft->getParent(), $grandparent); + + $this->assertSame($node->getParent(), $nodeChildLeft); + $this->assertSame($nodeChildLeft->getRight(), $node); + + $this->assertSame($node->getLeft(), $rightGrandChild); + $this->assertSame($rightGrandChild->getParent(), $node); + + $this->assertSame($node->getRight(), $nodeChildRight); + $this->assertSame($nodeChildRight->getParent(), $node); + } + public function testRotateLeft(): void { $grandparent = new RedBlackNode(10); @@ -238,6 +270,38 @@ public function testRotateLeft(): void $this->assertSame($rightGrandChild->getParent(), $nodeChildRight); } + public function testRotateWithLeft(): void + { + $grandparent = new RedBlackNode(10); + $redBlackTree = new RedBlackTree($grandparent); + $node = new RedBlackNode(5, $grandparent); + $grandparent->setLeft($node); + + $nodeChildLeft = new RedBlackNode(3, $node); + $node->setLeft($nodeChildLeft); + $nodeChildRight = new RedBlackNode(7, $node); + $node->setRight($nodeChildRight); + + $leftGrandChild = new RedBlackNode(6, $nodeChildRight); + $nodeChildRight->setLeft($leftGrandChild); + $rightGrandChild = new RedBlackNode(8, $nodeChildRight); + $nodeChildRight->setRight($rightGrandChild); + + $node->rotate($redBlackTree, Direction::Left); + + $this->assertSame($grandparent->getLeft(), $nodeChildRight); + $this->assertSame($nodeChildRight->getParent(), $grandparent); + + $this->assertSame($nodeChildRight->getLeft(), $node); + $this->assertSame($node->getParent(), $nodeChildRight); + + $this->assertSame($node->getRight(), $leftGrandChild); + $this->assertSame($leftGrandChild->getParent(), $node); + + $this->assertSame($nodeChildRight->getRight(), $rightGrandChild); + $this->assertSame($rightGrandChild->getParent(), $nodeChildRight); + } + private function isBalancedRedBlackTree(?RedBlackNode $node): bool { if ($node === null) { From 70ff857d765f3dd05f7be7062dc20ae0a0e0c0cb Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 02:52:53 +0100 Subject: [PATCH 77/90] refactor rotateRight and rotateLeft to new rotate helper --- src/helper/RedBlackNode.php | 22 ------------- src/helper/RedBlackTree.php | 12 +++---- tests/RedBlackTreeTest.php | 64 ------------------------------------- 3 files changed, 6 insertions(+), 92 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 9a32d98..35e4cfb 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -103,28 +103,6 @@ public function rotate(RedBlackTree $tree, Direction $direction): void $this->{$direction->opposite()->operation('set')}($grandchild); } - public function rotateRight(RedBlackTree $tree): void - { - if (!$this->left) { - return; - } - $leftRightGrandchild = $this->left->getRight(); - $this->updateParent($tree, Direction::Left); - $this->left->setRight($this); - $this->setLeft($leftRightGrandchild); - } - - public function rotateLeft(RedBlackTree $tree): void - { - if (!$this->right) { - return; - } - $rightLeftGrandchild = $this->right->getLeft(); - $this->updateParent($tree, Direction::Right); - $this->right->setLeft($this); - $this->setRight($rightLeftGrandchild); - } - /** * Update the parent of the node to the child in the $direction * should $this be root (both child check null), update parent to null and set child in $direction as new root diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 06d8dba..fa2db32 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -38,27 +38,27 @@ public function insert(int $value): void continue; } if ($pointer->getParent()->isChild(Direction::Left) && $pointer->isChild(Direction::Left)) { - $pointer->getParent()->getParent()->rotateRight($this); + $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()->rotateLeft($this); - $pointer->getParent()->rotateRight($this); + $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()->rotateLeft($this); + $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()->rotateRight($this); - $pointer->getParent()->rotateLeft($this); + $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); diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index 86ff01f..b8bba79 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -174,38 +174,6 @@ public function testSibling(): void $this->assertEquals($left, $right->sibling()); } - public function testRotateRight(): void - { - $grandparent = new RedBlackNode(10); - $redBlackTree = new RedBlackTree($grandparent); - $node = new RedBlackNode(5, $grandparent); - $grandparent->setLeft($node); - - $nodeChildLeft = new RedBlackNode(3, $node); - $node->setLeft($nodeChildLeft); - $nodeChildRight = new RedBlackNode(7, $node); - $node->setRight($nodeChildRight); - - $leftGrandChild = new RedBlackNode(1, $nodeChildLeft); - $nodeChildLeft->setLeft($leftGrandChild); - $rightGrandChild = new RedBlackNode(4, $nodeChildLeft); - $nodeChildLeft->setRight($rightGrandChild); - - $node->rotateRight($redBlackTree); - - $this->assertSame($grandparent->getLeft(), $nodeChildLeft); - $this->assertSame($nodeChildLeft->getParent(), $grandparent); - - $this->assertSame($node->getParent(), $nodeChildLeft); - $this->assertSame($nodeChildLeft->getRight(), $node); - - $this->assertSame($node->getLeft(), $rightGrandChild); - $this->assertSame($rightGrandChild->getParent(), $node); - - $this->assertSame($node->getRight(), $nodeChildRight); - $this->assertSame($nodeChildRight->getParent(), $node); - } - public function testRotateWithRight(): void { $grandparent = new RedBlackNode(10); @@ -238,38 +206,6 @@ public function testRotateWithRight(): void $this->assertSame($nodeChildRight->getParent(), $node); } - public function testRotateLeft(): void - { - $grandparent = new RedBlackNode(10); - $redBlackTree = new RedBlackTree($grandparent); - $node = new RedBlackNode(5, $grandparent); - $grandparent->setLeft($node); - - $nodeChildLeft = new RedBlackNode(3, $node); - $node->setLeft($nodeChildLeft); - $nodeChildRight = new RedBlackNode(7, $node); - $node->setRight($nodeChildRight); - - $leftGrandChild = new RedBlackNode(6, $nodeChildRight); - $nodeChildRight->setLeft($leftGrandChild); - $rightGrandChild = new RedBlackNode(8, $nodeChildRight); - $nodeChildRight->setRight($rightGrandChild); - - $node->rotateLeft($redBlackTree); - - $this->assertSame($grandparent->getLeft(), $nodeChildRight); - $this->assertSame($nodeChildRight->getParent(), $grandparent); - - $this->assertSame($nodeChildRight->getLeft(), $node); - $this->assertSame($node->getParent(), $nodeChildRight); - - $this->assertSame($node->getRight(), $leftGrandChild); - $this->assertSame($leftGrandChild->getParent(), $node); - - $this->assertSame($nodeChildRight->getRight(), $rightGrandChild); - $this->assertSame($rightGrandChild->getParent(), $nodeChildRight); - } - public function testRotateWithLeft(): void { $grandparent = new RedBlackNode(10); From 4c7b2b620c8b9e1cec5cdd18ef5f6599b90db96b Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 02:57:46 +0100 Subject: [PATCH 78/90] refactor uncle to use sibling helper --- src/helper/RedBlackNode.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index 35e4cfb..a579cf3 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -77,9 +77,7 @@ public function uncle(): ?RedBlackNode if (!$this->parent || !$this->parent->getParent()) { return null; } - return $this->parent->isChild(Direction::Left) - ? $this->parent->getParent()->getRight() - : $this->parent->getParent()->getLeft(); + return $this->parent->sibling(); } public function sibling(): ?RedBlackNode From 947b8d321b4dbb6310df2a807841e17f46cbc54a Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 03:02:05 +0100 Subject: [PATCH 79/90] add and update PHPDocs --- src/helper/RedBlackNode.php | 32 +++++++++++++++++++++++++++++++- src/helper/RedBlackTree.php | 9 +++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/helper/RedBlackNode.php b/src/helper/RedBlackNode.php index a579cf3..8a91da7 100644 --- a/src/helper/RedBlackNode.php +++ b/src/helper/RedBlackNode.php @@ -7,6 +7,13 @@ 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, @@ -62,6 +69,12 @@ public function setRight(?RedBlackNode $node): void $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) { @@ -72,6 +85,11 @@ public function isChild(Direction $direction): bool return false; } + /** + * Returns the sibling of the parent of $this + * + * @return RedBlackNode|null + */ public function uncle(): ?RedBlackNode { if (!$this->parent || !$this->parent->getParent()) { @@ -80,6 +98,11 @@ public function uncle(): ?RedBlackNode return $this->parent->sibling(); } + /** + * Return the opposite child of the parents to $this + * + * @return RedBlackNode|null + */ public function sibling(): ?RedBlackNode { if (!$this->parent) { @@ -90,6 +113,13 @@ public function sibling(): ?RedBlackNode : $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}) { @@ -103,7 +133,7 @@ public function rotate(RedBlackTree $tree, Direction $direction): void /** * Update the parent of the node to the child in the $direction - * should $this be root (both child check null), update parent to null and set child in $direction as new root + * 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 diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index fa2db32..985ec98 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -7,6 +7,9 @@ class RedBlackTree { + /** + * @param RedBlackNode|null $root + */ public function __construct(private ?RedBlackNode $root = null) {} public function getRoot(): ?RedBlackNode @@ -19,6 +22,12 @@ 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) { From 1063bad29e66955459d381436a3b0d2313ce424c Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 03:02:25 +0100 Subject: [PATCH 80/90] change visibility of binaryTreeInsert to private --- src/helper/RedBlackTree.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 985ec98..96924dc 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -84,7 +84,7 @@ public function insert(int $value): void * @param int $value * @return RedBlackNode */ - public function binaryTreeInsert(RedBlackNode $pointer, int $value): RedBlackNode + private function binaryTreeInsert(RedBlackNode $pointer, int $value): RedBlackNode { $node = new RedBlackNode($value); do { From d78eafe4ee711ce9f2861e015eec9e0dcf9fbedb Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 18:41:31 +0100 Subject: [PATCH 81/90] deletion base setup --- src/helper/RedBlackTree.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 96924dc..3537ea7 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -77,6 +77,17 @@ public function insert(int $value): void } while ($pointer); } + /** + * Delete the provided node from the tree + * + * @param RedBlackNode $node + * @return void + */ + public function delete(RedBlackNode $node): void + { + + } + /** * BinaryTree insert of $value starting at $pointer * From 415f7c146965b70405d1dc32db037be98e03e1ae Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 19:01:52 +0100 Subject: [PATCH 82/90] add binaryTreeDeletion --- src/helper/RedBlackTree.php | 40 ++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 3537ea7..a2c3a83 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -85,7 +85,7 @@ public function insert(int $value): void */ public function delete(RedBlackNode $node): void { - + $this->binaryTreeDelete($node); } /** @@ -114,4 +114,42 @@ private function binaryTreeInsert(RedBlackNode $pointer, int $value): RedBlackNo } } while (true); } + + /** + * BinaryTree deletion of $node + * + * @param RedBlackNode $node + * @return void + */ + private function binaryTreeDelete(RedBlackNode $node): void + { + if (!$node->getRight() && !$node->getLeft()) { + if ($node === $this->root) { + $this->root = null; + return; + } + if ($node->getParent()->isChild(Direction::Left)) { + $node->getParent()->setLeft(null); + return; + } + $node->getParent()->setRight(null); + return; + } + if ($node->getLeft() && !$node->getRight()) { + if ($node->getParent()->isChild(Direction::Left)) { + $node->getParent()->setLeft($node->getLeft()); + return; + } + $node->getParent()->setRight($node->getLeft()); + return; + } + if ($node->getRight() && !$node->getLeft()) { + if ($node->getParent()->isChild(Direction::Left)) { + $node->getParent()->setLeft($node->getRight()); + return; + } + $node->getParent()->setRight($node->getRight()); + return; + } + } } From 091f255dc3021153788ca0c50229e1dfa26f46ec Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 19:40:58 +0100 Subject: [PATCH 83/90] add two child case to binaryTreeDelete --- src/helper/RedBlackTree.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index a2c3a83..04e77ea 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -135,6 +135,23 @@ private function binaryTreeDelete(RedBlackNode $node): void $node->getParent()->setRight(null); return; } + if ($node->getLeft() && $node->getRight()) { + $pointer = $node->getRight(); + while ($pointer->getLeft()) { + $pointer = $pointer->getLeft(); + } + if ($pointer->getParent()->isChild(Direction::Left)) { + $pointer->getParent()->setLeft(null); + } else { + $pointer->getParent()->setRight(null); + } + if ($node->getParent()->isChild(Direction::Left)) { + $node->getParent()->setLeft($pointer); + return; + } + $node->getParent()->setRight($pointer); + return; + } if ($node->getLeft() && !$node->getRight()) { if ($node->getParent()->isChild(Direction::Left)) { $node->getParent()->setLeft($node->getLeft()); From 1a75d016cb8c69b98f72e633887bfaa56f256ca7 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 19:41:48 +0100 Subject: [PATCH 84/90] remove unneeded returns --- src/helper/RedBlackTree.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index 04e77ea..d8bb9aa 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -133,7 +133,6 @@ private function binaryTreeDelete(RedBlackNode $node): void return; } $node->getParent()->setRight(null); - return; } if ($node->getLeft() && $node->getRight()) { $pointer = $node->getRight(); @@ -150,7 +149,6 @@ private function binaryTreeDelete(RedBlackNode $node): void return; } $node->getParent()->setRight($pointer); - return; } if ($node->getLeft() && !$node->getRight()) { if ($node->getParent()->isChild(Direction::Left)) { @@ -158,7 +156,6 @@ private function binaryTreeDelete(RedBlackNode $node): void return; } $node->getParent()->setRight($node->getLeft()); - return; } if ($node->getRight() && !$node->getLeft()) { if ($node->getParent()->isChild(Direction::Left)) { @@ -166,7 +163,6 @@ private function binaryTreeDelete(RedBlackNode $node): void return; } $node->getParent()->setRight($node->getRight()); - return; } } } From 8fee520ff62be3e6770a8aa3880542af4bf2d8fe Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 19:44:58 +0100 Subject: [PATCH 85/90] move root check up and introduce parentChildDirection --- src/helper/RedBlackTree.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index d8bb9aa..bed160a 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -123,11 +123,14 @@ private function binaryTreeInsert(RedBlackNode $pointer, int $value): RedBlackNo */ private function binaryTreeDelete(RedBlackNode $node): void { + if ($node === $this->root) { + $this->root = null; + return; + } + $parentChildDirection = $node->getParent()->isChild(Direction::Left) + ? Direction::Left + : Direction::Right; if (!$node->getRight() && !$node->getLeft()) { - if ($node === $this->root) { - $this->root = null; - return; - } if ($node->getParent()->isChild(Direction::Left)) { $node->getParent()->setLeft(null); return; From 27c7d34df996ff0a793aef2721112da78e1da645 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Tue, 17 Dec 2024 19:48:29 +0100 Subject: [PATCH 86/90] refactor all isChild checks to usage of $parentChildDirection --- src/helper/RedBlackTree.php | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index bed160a..fca9851 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -131,11 +131,7 @@ private function binaryTreeDelete(RedBlackNode $node): void ? Direction::Left : Direction::Right; if (!$node->getRight() && !$node->getLeft()) { - if ($node->getParent()->isChild(Direction::Left)) { - $node->getParent()->setLeft(null); - return; - } - $node->getParent()->setRight(null); + $node->getParent()->{$parentChildDirection->operation('set')}(null); } if ($node->getLeft() && $node->getRight()) { $pointer = $node->getRight(); @@ -147,25 +143,13 @@ private function binaryTreeDelete(RedBlackNode $node): void } else { $pointer->getParent()->setRight(null); } - if ($node->getParent()->isChild(Direction::Left)) { - $node->getParent()->setLeft($pointer); - return; - } - $node->getParent()->setRight($pointer); + $node->getParent()->{$parentChildDirection->operation('set')}($pointer); } if ($node->getLeft() && !$node->getRight()) { - if ($node->getParent()->isChild(Direction::Left)) { - $node->getParent()->setLeft($node->getLeft()); - return; - } - $node->getParent()->setRight($node->getLeft()); + $node->getParent()->{$parentChildDirection->operation('set')}($node->getLeft()); } if ($node->getRight() && !$node->getLeft()) { - if ($node->getParent()->isChild(Direction::Left)) { - $node->getParent()->setLeft($node->getRight()); - return; - } - $node->getParent()->setRight($node->getRight()); + $node->getParent()->{$parentChildDirection->operation('set')}($node->getRight()); } } } From 8eba6ec0a888faaa8a2114ff2992aaa86fe6e677 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Wed, 18 Dec 2024 09:03:05 +0100 Subject: [PATCH 87/90] fix two child case relation updates --- src/helper/RedBlackTree.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index fca9851..df624aa 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -139,11 +139,12 @@ private function binaryTreeDelete(RedBlackNode $node): void $pointer = $pointer->getLeft(); } if ($pointer->getParent()->isChild(Direction::Left)) { - $pointer->getParent()->setLeft(null); + $pointer->getParent()->setLeft($pointer->getRight()); } else { - $pointer->getParent()->setRight(null); + $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()); From d0fc7b81b23ab26fa398ad984b43a7aa082b3987 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Wed, 18 Dec 2024 09:36:10 +0100 Subject: [PATCH 88/90] add basic deletion testcases --- tests/RedBlackTreeTest.php | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index b8bba79..8400572 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -238,6 +238,45 @@ public function testRotateWithLeft(): void $this->assertSame($rightGrandChild->getParent(), $nodeChildRight); } + public function testDeleteRootNode(): void + { + $tree = new RedBlackTree(); + + $tree->insert(10); + + $tree->delete($tree->getRoot()); + + $this->assertNull($tree->getRoot()); + } + + public function testDeleteLeftLeafNode(): void + { + $tree = new RedBlackTree(); + + $tree->insert(10); + $tree->insert(5); + $tree->insert(15); + + $leafNode = $tree->getRoot()->getLeft(); + $tree->delete($leafNode); + + $this->assertNull($tree->getRoot()->getLeft()); + } + + public function testDeleteRightLeafNode(): void + { + $tree = new RedBlackTree(); + + $tree->insert(10); + $tree->insert(5); + $tree->insert(15); + + $leafNode = $tree->getRoot()->getRight(); + $tree->delete($leafNode); + + $this->assertNull($tree->getRoot()->getRight()); + } + private function isBalancedRedBlackTree(?RedBlackNode $node): bool { if ($node === null) { From 1b15022a9b086fa5fbe6b0fe5ba1f3d8c2f2fd84 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Wed, 18 Dec 2024 09:36:34 +0100 Subject: [PATCH 89/90] fix parentChildDirection definition --- src/helper/RedBlackTree.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/RedBlackTree.php b/src/helper/RedBlackTree.php index df624aa..e6f068b 100644 --- a/src/helper/RedBlackTree.php +++ b/src/helper/RedBlackTree.php @@ -127,7 +127,7 @@ private function binaryTreeDelete(RedBlackNode $node): void $this->root = null; return; } - $parentChildDirection = $node->getParent()->isChild(Direction::Left) + $parentChildDirection = $node->isChild(Direction::Left) ? Direction::Left : Direction::Right; if (!$node->getRight() && !$node->getLeft()) { From 130900ba6d1d15dff7f3c1e2cd5a70c31171d4e0 Mon Sep 17 00:00:00 2001 From: dimitrijjedich Date: Wed, 18 Dec 2024 09:46:59 +0100 Subject: [PATCH 90/90] add first tests for one and two child cases --- tests/RedBlackTreeTest.php | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/RedBlackTreeTest.php b/tests/RedBlackTreeTest.php index 8400572..42dd029 100644 --- a/tests/RedBlackTreeTest.php +++ b/tests/RedBlackTreeTest.php @@ -263,6 +263,38 @@ public function testDeleteLeftLeafNode(): void $this->assertNull($tree->getRoot()->getLeft()); } + public function testDeleteNodeWithOneChild(): void + { + $tree = new RedBlackTree(); + + $tree->insert(10); + $tree->insert(15); + $tree->insert(5); + $tree->insert(2); + + $nodeWithOneChild = $tree->getRoot()->getLeft(); + $tree->delete($nodeWithOneChild); + + $this->assertEquals(2, $tree->getRoot()->getLeft()->getValue()); + } + + public function testDeleteNodeWithTwoChildren(): void + { + $tree = new RedBlackTree(); + $tree->insert(4); + $tree->insert(2); + $tree->insert(6); + $tree->insert(1); + $tree->insert(3); + $tree->insert(5); + $tree->insert(7); + + $nodeWithTwoChildren = $tree->getRoot()->getLeft(); + $tree->delete($nodeWithTwoChildren); + $this->assertEquals(1, $tree->getRoot()->getLeft()->getValue()); + $this->assertNull($tree->getRoot()->getLeft()->getLeft()); + } + public function testDeleteRightLeafNode(): void { $tree = new RedBlackTree();