From 08a2f05718a94cfa0eb73789de38208e6efe5d7d Mon Sep 17 00:00:00 2001 From: Yoanm <4410697+yoanm@users.noreply.github.com> Date: Wed, 22 Oct 2025 12:27:33 +0200 Subject: [PATCH 1/5] Add binary tree traversal, data-structure and factory --- .../BinaryTree/RecursiveReversedTraversal.php | 168 +++++++++ .../BinaryTree/RecursiveTraversal.php | 213 ++++++++++++ .../BinaryTree/ReversedTraversal.php | 257 ++++++++++++++ src/Algorithm/BinaryTree/Traversal.php | 320 ++++++++++++++++++ src/DataStructure/BinaryTree/Node.php | 18 + .../BinaryTree/NodeInterface.php | 16 + src/Factory/BinaryTreeFactory.php | 60 ++++ .../BinaryTree/AbstractTraversalTestCase.php | 195 +++++++++++ .../RecursiveReversedTraversalTest.php | 62 ++++ .../BinaryTree/RecursiveTraversalTest.php | 82 +++++ .../BinaryTree/ReversedTraversalTest.php | 78 +++++ .../Algorithm/BinaryTree/TraversalTest.php | 94 +++++ .../Algorithm/TraversalTestTrait.php | 42 +++ .../DataStructure/BinaryTree/NodeTest.php | 25 ++ .../Factory/BinaryTreeFactoryTest.php | 71 ++++ 15 files changed, 1701 insertions(+) create mode 100644 src/Algorithm/BinaryTree/RecursiveReversedTraversal.php create mode 100644 src/Algorithm/BinaryTree/RecursiveTraversal.php create mode 100644 src/Algorithm/BinaryTree/ReversedTraversal.php create mode 100644 src/Algorithm/BinaryTree/Traversal.php create mode 100644 src/DataStructure/BinaryTree/Node.php create mode 100644 src/DataStructure/BinaryTree/NodeInterface.php create mode 100644 src/Factory/BinaryTreeFactory.php create mode 100644 tests/Technical/Algorithm/BinaryTree/AbstractTraversalTestCase.php create mode 100644 tests/Technical/Algorithm/BinaryTree/RecursiveReversedTraversalTest.php create mode 100644 tests/Technical/Algorithm/BinaryTree/RecursiveTraversalTest.php create mode 100644 tests/Technical/Algorithm/BinaryTree/ReversedTraversalTest.php create mode 100644 tests/Technical/Algorithm/BinaryTree/TraversalTest.php create mode 100644 tests/Technical/Algorithm/TraversalTestTrait.php create mode 100644 tests/Technical/DataStructure/BinaryTree/NodeTest.php create mode 100644 tests/Technical/Factory/BinaryTreeFactoryTest.php diff --git a/src/Algorithm/BinaryTree/RecursiveReversedTraversal.php b/src/Algorithm/BinaryTree/RecursiveReversedTraversal.php new file mode 100644 index 0000000..a942bd9 --- /dev/null +++ b/src/Algorithm/BinaryTree/RecursiveReversedTraversal.php @@ -0,0 +1,168 @@ + + * โš  Recursive function implies a growing stack trace, which has a limited size!
+ * See {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\ReversedTraversal} for iterative implementations. + * + * + *
+ * โ„น Reversed level-order is not implemented as it implies to keep track of and look at each and every node in + * order to detect the last level => Time and space complexity will be ๐‘‚โŸฎ๐‘›โŸฏ in any cases !
+ * Workaround:
+ * 1. Fetch the result of {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::levelOrder} + * or {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::levelOrderGenerator} + * 2. Either start from the end or reverse the whole list (the latter implies + * an additional ๐‘‚โŸฎ๐‘›โŸฏ time complexity though !) + * + * + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\ReversedTraversal for iterative implementations. + * + * + * @template TNode of Node The actual Node class + */ +class RecursiveReversedTraversal +{ + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal::preOrderGenerator() + * + * @param TNode $node + * + * @return list + * + */ + public static function preOrder(Node $node): array + { + // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + return iterator_to_array(self::preOrderGenerator($node), false); + } + + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal::preOrderGenerator() + * + * @param TNode $node + * + * @return list + */ + public static function inOrder(Node $node): array + { + // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + return iterator_to_array(self::inOrderGenerator($node), false); + } + + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal::inOrderGenerator() + * + * @param TNode $node + * + * @return list + */ + public static function postOrder(Node $node): array + { + // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + return iterator_to_array(self::postOrderGenerator($node), false); + } + + /** + * Reversed Pre-order: N->R->L => Node, then Right children, then Left children + * + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a right child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! + * + * + * @param TNode $node + * + * @return Generator + */ + public static function preOrderGenerator(Node $node): Generator + { + yield $node; + + if (null !== $node->right) { + yield from self::preOrderGenerator($node->right); + } + if (null !== $node->left) { + yield from self::preOrderGenerator($node->left); + } + } + + /** + * Reversed In-order (=Reversed DFS): R->N->L => Right children, then Node, then Left children + * + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a right child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! + * + * + * @param TNode $node + * + * @return Generator + */ + public static function inOrderGenerator(Node $node): Generator + { + if (null !== $node->right) { + yield from self::inOrderGenerator($node->right); + } + + yield $node; + + if (null !== $node->left) { + yield from self::inOrderGenerator($node->left); + } + } + + /** + * Reversed Post-order: R->L->N => Right children, then Left children, then Node + * + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a right child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! + * + * + * @param TNode $node + * + * @return Generator + */ + public static function postOrderGenerator(Node $node): Generator + { + if (null !== $node->right) { + yield from self::postOrderGenerator($node->right); + } + if (null !== $node->left) { + yield from self::postOrderGenerator($node->left); + } + yield $node; + } +} diff --git a/src/Algorithm/BinaryTree/RecursiveTraversal.php b/src/Algorithm/BinaryTree/RecursiveTraversal.php new file mode 100644 index 0000000..51460d2 --- /dev/null +++ b/src/Algorithm/BinaryTree/RecursiveTraversal.php @@ -0,0 +1,213 @@ + + * โš  Recursive function implies a growing stack trace, which has a limited size!
+ * See {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal} for iterative implementations. + * + * + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal for iterative implementations. + * + * + * @template TNode of Node The actual Node class + */ +class RecursiveTraversal +{ + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::preOrderGenerator() + * + * @param TNode $node + * + * @return list + */ + public static function preOrder(Node $node): array + { + // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + return iterator_to_array(self::preOrderGenerator($node), false); + } + + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::preOrderGenerator() + * + * @param TNode $node + * + * @return list + */ + public static function inOrder(Node $node): array + { + // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + return iterator_to_array(self::inOrderGenerator($node), false); + } + + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::inOrderGenerator() + * + * @param TNode $node + * + * @return list + */ + public static function postOrder(Node $node): array + { + // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + return iterator_to_array(self::postOrderGenerator($node), false); + } + + /** + * Level order (=BFS): Traverse tree level by level, from Left to Right + * + * + *
+ * ๐Ÿ’ก Reverse the list for reversed level-order traversal ! + *
+ *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐‘›โŸฏ - Due to the list storing every node for every level (see levelOrderHelper() function). + * + * @return list> key is the level, value is the list of nodes for that level + */ + public static function levelOrder(Node $root): array + { + $res = []; + + self::levelOrderHelper($root, $res); + + return $res; + } + + /** + * Pre-order: N->L->R => Node, then Left children, then Right children + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a left child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! + * + * @param TNode $node + * + * @return Generator + */ + public static function preOrderGenerator(Node $node): Generator + { + yield $node; + + if (null !== $node->left) { + yield from self::preOrderGenerator($node->left); + } + if (null !== $node->right) { + yield from self::preOrderGenerator($node->right); + } + } + + /** + * In-order (=DFS): L->N->R => Left children, then Node, then Right children + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a left child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! + * + * @param TNode $node + * + * @return Generator + */ + public static function inOrderGenerator(Node $node): Generator + { + if (null !== $node->left) { + yield from self::inOrderGenerator($node->left); + } + + yield $node; + + if (null !== $node->right) { + yield from self::inOrderGenerator($node->right); + } + } + + /** + * Post-order: L->R->N => Left children, then Right children, then Node + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a left child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! + * + * @param TNode $node + * + * @return Generator + */ + public static function postOrderGenerator(Node $node): Generator + { + if (null !== $node->left) { + yield from self::postOrderGenerator($node->left); + } + if (null !== $node->right) { + yield from self::postOrderGenerator($node->right); + } + yield $node; + } + + /** + * Level order (=BFS): Traverse tree level by level, from Left to Right + * + * + *
+ * ๐Ÿ’ก Reverse the list for reversed level-order traversal ! + *
+ *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a left child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎhโŸฏ - Due to the stack trace (recursive calls) + * + * @param TNode $node + * + * @param array> $res key is the level, value is the list of nodes for that level + */ + public static function levelOrderHelper(Node $node, array &$res, int $level = 0): void + { + $res[$level] ??= []; // In case current level hasn't been seen/managed yet + + $res[$level][] = $node; + if (null !== $node->left) { + self::levelOrderHelper($node->left, $res, $level + 1); + } + if (null !== $node->right) { + self::levelOrderHelper($node->right, $res, $level + 1); + } + } +} diff --git a/src/Algorithm/BinaryTree/ReversedTraversal.php b/src/Algorithm/BinaryTree/ReversedTraversal.php new file mode 100644 index 0000000..ed51b50 --- /dev/null +++ b/src/Algorithm/BinaryTree/ReversedTraversal.php @@ -0,0 +1,257 @@ + + * โ„น Reversed level-order is not implemented as it implies to keep track of and look at each and every node in + * order to detect the last level => Time and space complexity will be ๐‘‚โŸฎ๐‘›โŸฏ in any cases !
+ * Workaround:
+ * 1. Fetch the result of {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::levelOrder} + * or {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::levelOrderGenerator} + * 2. Either start from the end or reverse the whole list (the latter implies + * an additional ๐‘‚โŸฎ๐‘›โŸฏ time complexity though !) + * + * + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal for recursive implementations. + * + * + * @template TNode of Node The actual Node class + */ +class ReversedTraversal +{ + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RevervedTraversal::preOrderGenerator() + * + * @param TNode $node + * + * @return list + */ + public static function preOrder(Node $node): array + { + return iterator_to_array(self::preOrderGenerator($node)); + } + + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RevervedTraversal::inOrderGenerator() + * + * @param TNode $node + * + * @return list + */ + public static function inOrder(Node $node): array + { + return iterator_to_array(self::inOrderGenerator($node)); + } + + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RevervedTraversal::postOrderGenerator() + * + * @param TNode $node + * + * @return list + */ + public static function postOrder(Node $node): array + { + return iterator_to_array(self::postOrderGenerator($node)); + } + + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RevervedTraversal::BFSGenerator() + * + * @param TNode $node + * + * @return list + */ + public static function BFS(Node $node): array + { + return iterator_to_array(self::BFSGenerator($node)); + } + + /** + * Reversed Pre-order: N->R->L => Node, then Right children, then Left children + * + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a right child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes path up to the root node. + * + * + * @param TNode $node + * + * @return Generator + */ + public static function preOrderGenerator(Node $node): Generator + { + $stack = new SplStack(); + + $stack->push($node); + while (!$stack->isEmpty()) { + /** @var Node $currentNode */ + $currentNode = $stack->pop(); + + yield $currentNode; + + if (null !== $currentNode->left) { + $stack->push($currentNode->left); + } + if (null !== $currentNode->right) { + $stack->push($currentNode->right); + } + } + } + + /** + * Reversed In-order (=Reversed DFS): R->N->L => Right children, then Node, then Left children + * + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a right child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes path up to the root node. + * + * + * @param TNode $node + * + * @return Generator + */ + public static function inOrderGenerator(Node $node): Generator + { + $stack = new SplStack(); + + $currentNode = $node; + while (null !== $currentNode || !$stack->isEmpty()) { + while (null !== $currentNode) { + $stack->push($currentNode); + $currentNode = $currentNode->right; + } + + // Current node becomes the rightmost leaf found + // (or the same current node in case it doesn't have right node!) + /** @var Node $currentNode */ + $currentNode = $stack->pop(); + + yield $currentNode; + + // Right is now managed, let's take a look on left side + $currentNode = $currentNode->left; + } + } + + /** + * Reversed Post-order: R->L->N => Right children, then Left children, then Node + * + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a right child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes. + * + * + * @param TNode $node + * + * @return Generator + */ + public static function postOrderGenerator(Node $node): Generator + { + $stack = new SplStack(); + + $currentNode = $node; + while (null !== $currentNode || !$stack->isEmpty()) { + while (null !== $currentNode) { + if (null !== $currentNode->left) { + $stack->push($currentNode->left); + } + $stack->push($currentNode); + $currentNode = $currentNode->right; + } + + /** @var Node $currentNode */ + $currentNode = $stack->pop(); + + if (!$stack->isEmpty() && $currentNode->left === $stack->top()) { + // Remove current node left child from the stack, it will become $currentNode for next iteration + $stack->pop(); + + $stack->push($currentNode); + $currentNode = $currentNode->left; + } else { + yield $currentNode; + + $currentNode = null; + } + } + } + + /** + * Reversed BFS: Traverse tree level by level, from Right to Left.
+ * + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐‘š the number of nodes for the bigger level + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐‘šโŸฏ - Due to the queue storing current level parent nodes + * + * + * @param TNode $node + * + * @return Generator + */ + public static function BFSGenerator(Node $node): Generator + { + $queue = new SplQueue(); + + $queue->enqueue($node); + while (!$queue->isEmpty()) { + $currentLevelNodeCounter = $queue->count(); + while ($currentLevelNodeCounter > 0) { + $node = $queue->dequeue(); + + yield $node; + + if (null !== $node->right) { + $queue->enqueue($node->right); + } + if (null !== $node->left) { + $queue->enqueue($node->left); + } + + --$currentLevelNodeCounter; + } + } + } +} diff --git a/src/Algorithm/BinaryTree/Traversal.php b/src/Algorithm/BinaryTree/Traversal.php new file mode 100644 index 0000000..628dca6 --- /dev/null +++ b/src/Algorithm/BinaryTree/Traversal.php @@ -0,0 +1,320 @@ + + */ + public static function preOrder(Node $node): array + { + return iterator_to_array(self::preOrderGenerator($node)); + } + + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::inOrderGenerator() + * + * @param TNode $node + * + * @return list + */ + public static function inOrder(Node $node): array + { + return iterator_to_array(self::inOrderGenerator($node)); + } + + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::postOrderGenerator() + * + * @param TNode $node + * + * @return list + */ + public static function postOrder(Node $node): array + { + return iterator_to_array(self::postOrderGenerator($node)); + } + + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::BFSGenerator() + * + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::levelOrder() if node level must be known ! + * + * @param TNode $node + * + * @return list + */ + public static function BFS(Node $node): array + { + return iterator_to_array(self::BFSGenerator($node)); + } + + /** + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::levelOrderGenerator() + * + * + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::BFS() if node level isn't useful. + * + * @param TNode $node + * + * @return list> key is the level, value is the list of nodes for that level + */ + public static function levelOrder(Node $node): array + { + return iterator_to_array(self::levelOrderGenerator($node)); + } + + /** + * Pre-order: N->L->R => Node, then Left children, then Right children. + * + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a left child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes path up to the root node. + * + * + * @param TNode $node + * + * @return Generator + */ + public static function preOrderGenerator(Node $node): Generator + { + $stack = new SplStack(); + + $stack->push($node); + while (!$stack->isEmpty()) { + /** @var Node $currentNode */ + $currentNode = $stack->pop(); + + yield $currentNode; + + if (null !== $currentNode->right) { + $stack->push($currentNode->right); + } + if (null !== $currentNode->left) { + $stack->push($currentNode->left); + } + } + } + + /** + * In-order (=DFS): L->N->R => Left children, then Node, then Right children. + * + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a left child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes path up to the root node. + * + * + * @param TNode $node + * + * @return Generator + */ + public static function inOrderGenerator(Node $node): Generator + { + $stack = new SplStack(); + $currentNode = $node; + while (null !== $currentNode || !$stack->isEmpty()) { + while (null !== $currentNode) { + $stack->push($currentNode); + $currentNode = $currentNode->left; + } + + // Current node becomes the leftmost leaf found + // (or the same current node in case it doesn't have left node!) + /** @var Node $currentNode */ + $currentNode = $stack->pop(); + + yield $currentNode; + + // Left is now managed, let's take a look on right side + $currentNode = $currentNode->right; + } + } + + /** + * Post-order: L->R->N => Left children, then Right children, then Node. + * + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).
+ * โš  ๐˜ฉ = ๐‘› if every node has only a left child (skewed tree) + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes path up to the root node. + * + * + * @param TNode $node + * + * @return Generator + */ + public static function postOrderGenerator(Node $node): Generator + { + $stack = new SplStack(); + + $currentNode = $node; + while (null !== $currentNode || !$stack->isEmpty()) { + while (null !== $currentNode) { + if (null !== $currentNode->right) { + $stack->push($currentNode->right); + } + $stack->push($currentNode); + $currentNode = $currentNode->left; + } + + /** @var Node $currentNode */ + $currentNode = $stack->pop(); + + if (!$stack->isEmpty() && $currentNode->right === $stack->top()) { + // Remove current node right child from the stack, it will become $currentNode for next iteration + $stack->pop(); + + $stack->push($currentNode); + $currentNode = $currentNode->right; + } else { + yield $currentNode; + + $currentNode = null; + } + } + } + + /** + * Level order (=BFS): Traverse tree level by level, from Left to Right + * + * + *
+ * ๐Ÿ’ก Reverse the list for reversed level-order traversal ! + *
+ *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐‘š the number of nodes for the bigger level + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐‘šโŸฏ - Due to the queue storing current level parent nodes, plus temporary list storing every node + * for the current level + * + * + * + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::BFSGenerator() if node level isn't useful. + * + * + * @param TNode $node + * + * @return Generator> key is the level, value is the list of nodes for that level + */ + public static function levelOrderGenerator(Node $node): Generator + { + $queue = new SplQueue(); + + $queue->enqueue($node); + while (!$queue->isEmpty()) { + $nodeList = []; + $currentLevelNodeCounter = $queue->count(); + while ($currentLevelNodeCounter > 0) { + $node = $queue->dequeue(); + + $nodeList[] = $node; + + if (null !== $node->left) { + $queue->enqueue($node->left); + } + if (null !== $node->right) { + $queue->enqueue($node->right); + } + + --$currentLevelNodeCounter; + } + + yield $nodeList; + } + } + + /** + * BFS: Traverse tree level by level, from Left to Right.
+ * + * + *
+ * ### Time/Space complexity + * With: + * - ๐‘› the number of node in the tree + * - ๐‘š the number of nodes for the bigger level + * + * TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time + * + * SC: ๐‘‚โŸฎ๐‘šโŸฏ - Due to the queue storing current level parent nodes + * + * + * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::levelOrderGenerator() if node level must be known ! + * + * + * @param TNode $node + * + * @return Generator + */ + public static function BFSGenerator(Node $node): Generator + { + $queue = new SplQueue(); + + $queue->enqueue($node); + while (!$queue->isEmpty()) { + $currentLevelNodeCounter = $queue->count(); + while ($currentLevelNodeCounter > 0) { + $node = $queue->dequeue(); + + yield $node; + + if (null !== $node->left) { + $queue->enqueue($node->left); + } + if (null !== $node->right) { + $queue->enqueue($node->right); + } + + --$currentLevelNodeCounter; + } + } + } +} diff --git a/src/DataStructure/BinaryTree/Node.php b/src/DataStructure/BinaryTree/Node.php new file mode 100644 index 0000000..c30f146 --- /dev/null +++ b/src/DataStructure/BinaryTree/Node.php @@ -0,0 +1,18 @@ + $list + * @param TNodeCreator $nodeCreator + * + * @return null|Node|TNode + */ + public static function fromLevelOrderList(array $list, callable $nodeCreator): null|Node + { + $tailIdx = count($list) - 1; + if ($tailIdx < 0 || (0 === $tailIdx && null === $list[0])) { + return null; + } + + $queue = new SplQueue(); + + $root = call_user_func($nodeCreator, $list[0]); + $queue->enqueue($root); + + $idx = 1; // Root value already managed, hence 1 rather 0 ! + while ($idx <= $tailIdx) { + /** @var Node|TNode $parentNode */ + $parentNode = $queue->dequeue(); + + // 1. Manage left node value + if (null !== $list[$idx]) { + $parentNode->left = $node = call_user_func($nodeCreator, $list[$idx]); + $queue->enqueue($node); + } + ++$idx; + + // 1. Manage right node value (if it exists !) + if ($idx <= $tailIdx && null !== $list[$idx]) { + $parentNode->right = $node = call_user_func($nodeCreator, $list[$idx]); + $queue->enqueue($node); + } + ++$idx; + } + + return $root; + } +} diff --git a/tests/Technical/Algorithm/BinaryTree/AbstractTraversalTestCase.php b/tests/Technical/Algorithm/BinaryTree/AbstractTraversalTestCase.php new file mode 100644 index 0000000..a6d0a12 --- /dev/null +++ b/tests/Technical/Algorithm/BinaryTree/AbstractTraversalTestCase.php @@ -0,0 +1,195 @@ + [ + 'treeData' => [1,2,7,3,4,null,8,null,null,5,6,9], + 'expected' => [1,2,3,4,5,6,7,8,9], + ], + 'Basic case 2' => [ + 'treeData' => [1,2,7,3,6,8,10,4,5,null,null,null,9,11,12], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12], + ], + 'Skewed tree - only left' => [ + 'treeData' => [1,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9,null,10,null,11,null,12], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12], + ], + 'Skewed tree - only right' => [ + 'treeData' => [1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9,null,10,null,11,null,12], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12], + ], + 'Zigzag tree' => [ + 'treeData' => [1,2,null,null,3,4,null,null,5,6,null,null,7,8,null,null,9,10,null,null,11,12], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12], + ], + 'Double zigzag tree' => [ + 'treeData' => [1,2,8,null,3,9,null,4,null,null,10,null,5,11,null,6,null,null,12,null,7,13], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12,13], + ], + 'Asymmetric tree - deep left nesting' => [ + 'treeData' => [1,2,6,3,null,null,7,4,null,null,null,5], + 'expected' => [1,2,3,4,5,6,7], + ], + ]; + + return self::mapTestCases($caseDataList); + } + + public static function provideReversedPreOrderTestCases(): array + { + $caseDataList = [ + 'Basic case 1' => [ + 'treeData' => [1,5,2,9,6,null,3,null,null,8,7,4], + 'expected' => [1,2,3,4,5,6,7,8,9], + ], + 'Basic case 2' => [ + 'treeData' => [1,8,2,10,9,6,3,12,11,null,null,null,7,5,4], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12], + ], + ]; + + return self::mapTestCases($caseDataList); + } + + public static function provideInOrderTestCases(): array + { + $caseDataList = [ + 'Basic case 1' => [ + 'treeData' => [6,2,7,1,4,null,9,null,null,3,5,8], + 'expected' => [1,2,3,4,5,6,7,8,9], + ], + 'Basic case 2' => [ + 'treeData' => [6,4,9,2,5,7,11,1,3,null,null,null,8,10,12], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12], + ], + ]; + + return self::mapTestCases($caseDataList); + } + + public static function provideReversedInOrderTestCases(): array + { + $caseDataList = [ + 'Basic case 1' => [ + 'treeData' => [4,8,3,9,6,null,1,null,null,7,5,2], + 'expected' => [1,2,3,4,5,6,7,8,9], + ], + 'Basic case 2' => [ + 'treeData' => [7,9,4,11,8,6,2,12,10,null,null,null,5,3,1], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12], + ], + ]; + + return self::mapTestCases($caseDataList); + } + + public static function providePostorderTestCases(): array + { + $caseDataList = [ + 'Basic case 1' => [ + 'treeData' => [9,5,8,1,4,null,7,null,null,2,3,6], + 'expected' => [1,2,3,4,5,6,7,8,9], + ], + 'Basic case 2' => [ + 'treeData' => [12,5,11,3,4,7,10,1,2,null,null,null,6,8,9], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12], + ], + ]; + + return self::mapTestCases($caseDataList); + } + + public static function provideReversedPostorderTestCases(): array + { + $caseDataList = [ + 'Basic case 1' => [ + 'treeData' => [9,8,3,7,6,null,2,null,null,5,4,1], + 'expected' => [1,2,3,4,5,6,7,8,9], + ], + 'Basic case 2' => [ + 'treeData' => [12,11,6,10,7,5,3,9,8,null,null,null,4,2,1], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12], + ], + ]; + + return self::mapTestCases($caseDataList); + } + + public static function provideTestReversedBFSCases(): array + { + $caseDataList = [ + 'Basic case 1' => [ + 'treeData' => [1,3,2,6,5,null,4,null,null,9,8,7], + 'expected' => [1,2,3,4,5,6,7,8,9], + ], + 'Basic case 2' => [ + 'treeData' => [1,3,2,7,6,5,4,12,11,null,null,null,10,9,8], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12], + ], + 'Basic case 3' => [ + 'treeData' => [1,3,2,7,6,5,4,12,11,null,null,null,10,9,8,null,null,15,null,null,null,14,null,null,13,17,null,null,null,16], + 'expected' => [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17], + ], + ]; + + return self::mapTestCases($caseDataList); + } + + public static function provideTestLevelOrderCases(): array + { + $caseDataList = [ + 'Basic case 1' => [ + 'treeData' => [1,2,3,4,5,null,6,null,null,7,8,9], + 'expected' => [[1],[2,3],[4,5,6],[7,8,9]], + ], + 'Basic case 2' => [ + 'treeData' => [1,2,3,4,5,6,7,8,9,null,null,null,10,11,12], + 'expected' => [[1],[2,3],[4,5,6,7],[8,9,10,11,12]], + ], + 'Basic case 3' => [ + 'treeData' => [1,2,3,4,5,6,7,8,9,null,null,null,10,11,12,null,null,13,null,null,null,14,null,null,15,16,null,null,null,17], + 'expected' => [[1],[2,3],[4,5,6,7],[8,9,10,11,12],[13,14,15],[16,17]], + ], + ]; + + return self::mapTestCases($caseDataList); + } + + /** + * @param array $caseDataList + * + * @return array + */ + protected static function mapTestCases(array $caseDataList): array + { + return array_map( + static fn ($caseData) => [ + TreeFactory::fromLevelOrderList($caseData['treeData'], static fn(int $val) => new Node($val)), + $caseData['expected'], + ], + $caseDataList, + ); + } +} diff --git a/tests/Technical/Algorithm/BinaryTree/RecursiveReversedTraversalTest.php b/tests/Technical/Algorithm/BinaryTree/RecursiveReversedTraversalTest.php new file mode 100644 index 0000000..fa6940f --- /dev/null +++ b/tests/Technical/Algorithm/BinaryTree/RecursiveReversedTraversalTest.php @@ -0,0 +1,62 @@ + $expected + * @param iterable $actual + */ + protected static function assertSameTreeNodeValues(array $expected, iterable $actual, Assert $assert): void + { + $newActual = []; + foreach ($actual as $node) { + $newActual[] = $node->val; + } + + $assert::assertSame($expected, $newActual); + } + + /** + * @param list> $expected + * @param iterable> $actual + */ + protected static function assertLevelOrderSameTreeNodeValues(array $expected, iterable $actual, Assert $assert): void + { + $newActual = []; + foreach ($actual as $key => $nodeList) { + $newActual[$key] = []; + foreach ($nodeList as $node) { + $newActual[$key][] = $node->val; + } + } + + $assert::assertSame($expected, $newActual); + } +} diff --git a/tests/Technical/DataStructure/BinaryTree/NodeTest.php b/tests/Technical/DataStructure/BinaryTree/NodeTest.php new file mode 100644 index 0000000..eb7728a --- /dev/null +++ b/tests/Technical/DataStructure/BinaryTree/NodeTest.php @@ -0,0 +1,25 @@ +val); + self::assertSame($leftNode, $node->left); + self::assertSame($rightNode, $node->right); + } +} diff --git a/tests/Technical/Factory/BinaryTreeFactoryTest.php b/tests/Technical/Factory/BinaryTreeFactoryTest.php new file mode 100644 index 0000000..7448e26 --- /dev/null +++ b/tests/Technical/Factory/BinaryTreeFactoryTest.php @@ -0,0 +1,71 @@ + new Node($val)); + + self::assertNotNull($root); + self::assertInstanceOf(Node::class, $root); + self::assertLevelOrderSameTreeNodeValues($expected, Helper::levelOrder($root), $this); + } + + /** + * @dataProvider provideFromLevelOrderListWithEmptyListTestCases + */ + public function testFromLevelOrderListWithEmptyList(array $data): void + { + $root = Factory::fromLevelOrderList($data, static fn(int $val) => new Node($val)); + + self::assertNull($root); + } + + public function provideFromLevelOrderListWithEmptyListTestCases(): array + { + return [ + 'Empty list' => [ + 'data' => [], + ], + 'Only one null value' => [ + 'data' => [null], + ], + ]; + } + + public function provideFromLevelOrderListTestCases(): array + { + return [ + 'Basic case 1' => [ + 'data' => [1,2,3,4,5,null,6,null,null,7,8,9], + 'expected' => [[1],[2,3],[4,5,6],[7,8,9]], + ], + 'Basic case 2' => [ + 'data' => [1,2,3,4,5,6,7,8,9,null,null,null,10,11,12], + 'expected' => [[1],[2,3],[4,5,6,7],[8,9,10,11,12]], + ], + 'Basic case 3' => [ + 'data' => [1,2,3,4,5,6,7,8,9,null,null,null,10,11,12,null,null,13,null,null,null,14,null,null,15,16,null,null,null,17], + 'expected' => [[1],[2,3],[4,5,6,7],[8,9,10,11,12],[13,14,15],[16,17]], + ], + ]; + } +} From 69c02051f5f3b117edb7350b7038ed17f4c6e0f2 Mon Sep 17 00:00:00 2001 From: Yoanm <4410697+yoanm@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:08:57 +0200 Subject: [PATCH 2/5] Improve PHPDoc --- src/Algorithm/BinarySearch.php | 18 ++++++++++++------ .../BinaryTree/RecursiveReversedTraversal.php | 10 ++++++---- .../BinaryTree/RecursiveTraversal.php | 15 ++++++++++----- src/Algorithm/BinaryTree/ReversedTraversal.php | 12 ++++++++---- src/Algorithm/BinaryTree/Traversal.php | 18 ++++++++++++------ src/Factory/BinaryTreeFactory.php | 3 ++- .../Technical/Algorithm/TraversalTestTrait.php | 6 ++++-- 7 files changed, 54 insertions(+), 28 deletions(-) diff --git a/src/Algorithm/BinarySearch.php b/src/Algorithm/BinarySearch.php index e34cb30..11c2b29 100644 --- a/src/Algorithm/BinarySearch.php +++ b/src/Algorithm/BinarySearch.php @@ -21,8 +21,10 @@ class BinarySearch * SC: ๐‘‚โŸฎ๐ŸทโŸฏ - Constant extra space * * - * @param list|ArrayAccess $list โš  Must be sorted in non-decreasing order (minโ†’max)!
- * May contain duplicates. + * @param array|ArrayAccess $list โš  Must be a 0 indexed list, 0 to n consecutive indexes, + * sorted in non-decreasing order (minโ†’max)!
+ * May contain duplicates. + * @phpstan-param list|ArrayAccess $list * @param int|float $target * @param int $lowIdx Lookup start index.
* Default to 0 (head index). @@ -70,8 +72,10 @@ public static function find( * SC: ๐‘‚โŸฎ๐ŸทโŸฏ - Constant extra space * * - * @param list|ArrayAccess $list โš  Must be sorted in non-decreasing order (minโ†’max)!
- * May contain duplicates. + * @param array|ArrayAccess $list โš  Must be a 0 indexed list, 0 to n consecutive indexes, + * sorted in non-decreasing order (minโ†’max)!
+ * May contain duplicates. + * @phpstan-param list|ArrayAccess $list * @param int|float $target * @param int $lowIdx Lookup start index.
* Default to 0 (head index). @@ -119,8 +123,10 @@ public static function lowerBound( * SC: ๐‘‚โŸฎ๐ŸทโŸฏ - Constant extra space * * - * @param list|ArrayAccess $list โš  Must be sorted in non-decreasing order (minโ†’max)!
- * May contain duplicates. + * @param array|ArrayAccess $list โš  Must be a 0 indexed list, 0 to n consecutive indexes, + * sorted in non-decreasing order (minโ†’max)!
+ * May contain duplicates. + * @phpstan-param list|ArrayAccess $list * @param int|float $target * @param int $lowIdx Lookup start index.
* Default to 0 (head index). diff --git a/src/Algorithm/BinaryTree/RecursiveReversedTraversal.php b/src/Algorithm/BinaryTree/RecursiveReversedTraversal.php index a942bd9..80cd22d 100644 --- a/src/Algorithm/BinaryTree/RecursiveReversedTraversal.php +++ b/src/Algorithm/BinaryTree/RecursiveReversedTraversal.php @@ -35,8 +35,8 @@ class RecursiveReversedTraversal * * @param TNode $node * - * @return list - * + * @return array + * @phpstan-return list */ public static function preOrder(Node $node): array { @@ -49,7 +49,8 @@ public static function preOrder(Node $node): array * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function inOrder(Node $node): array { @@ -62,7 +63,8 @@ public static function inOrder(Node $node): array * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function postOrder(Node $node): array { diff --git a/src/Algorithm/BinaryTree/RecursiveTraversal.php b/src/Algorithm/BinaryTree/RecursiveTraversal.php index 51460d2..0c7fbe4 100644 --- a/src/Algorithm/BinaryTree/RecursiveTraversal.php +++ b/src/Algorithm/BinaryTree/RecursiveTraversal.php @@ -25,7 +25,8 @@ class RecursiveTraversal * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function preOrder(Node $node): array { @@ -38,7 +39,8 @@ public static function preOrder(Node $node): array * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function inOrder(Node $node): array { @@ -51,7 +53,8 @@ public static function inOrder(Node $node): array * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function postOrder(Node $node): array { @@ -75,7 +78,8 @@ public static function postOrder(Node $node): array * * SC: ๐‘‚โŸฎ๐‘›โŸฏ - Due to the list storing every node for every level (see levelOrderHelper() function). * - * @return list> key is the level, value is the list of nodes for that level + * @return array> key is the level, value is the list of nodes for that level + * @phpstan-return list> */ public static function levelOrder(Node $root): array { @@ -196,7 +200,8 @@ public static function postOrderGenerator(Node $node): Generator * * @param TNode $node * - * @param array> $res key is the level, value is the list of nodes for that level + * @param array> $res key is the level, value is the list of nodes for that level + * @phpstan-param array> $res */ public static function levelOrderHelper(Node $node, array &$res, int $level = 0): void { diff --git a/src/Algorithm/BinaryTree/ReversedTraversal.php b/src/Algorithm/BinaryTree/ReversedTraversal.php index ed51b50..466e470 100644 --- a/src/Algorithm/BinaryTree/ReversedTraversal.php +++ b/src/Algorithm/BinaryTree/ReversedTraversal.php @@ -35,7 +35,8 @@ class ReversedTraversal * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function preOrder(Node $node): array { @@ -47,7 +48,8 @@ public static function preOrder(Node $node): array * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function inOrder(Node $node): array { @@ -59,7 +61,8 @@ public static function inOrder(Node $node): array * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function postOrder(Node $node): array { @@ -71,7 +74,8 @@ public static function postOrder(Node $node): array * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function BFS(Node $node): array { diff --git a/src/Algorithm/BinaryTree/Traversal.php b/src/Algorithm/BinaryTree/Traversal.php index 628dca6..d2f29bb 100644 --- a/src/Algorithm/BinaryTree/Traversal.php +++ b/src/Algorithm/BinaryTree/Traversal.php @@ -25,7 +25,8 @@ class Traversal * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function preOrder(Node $node): array { @@ -37,7 +38,8 @@ public static function preOrder(Node $node): array * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function inOrder(Node $node): array { @@ -49,7 +51,8 @@ public static function inOrder(Node $node): array * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function postOrder(Node $node): array { @@ -63,7 +66,8 @@ public static function postOrder(Node $node): array * * @param TNode $node * - * @return list + * @return array + * @phpstan-return list */ public static function BFS(Node $node): array { @@ -78,7 +82,8 @@ public static function BFS(Node $node): array * * @param TNode $node * - * @return list> key is the level, value is the list of nodes for that level + * @return array> key is the level, value is the list of nodes for that level + * @phpstan-return list> */ public static function levelOrder(Node $node): array { @@ -243,7 +248,8 @@ public static function postOrderGenerator(Node $node): Generator * * @param TNode $node * - * @return Generator> key is the level, value is the list of nodes for that level + * @return Generator> key is the level, value is the list of nodes for that level + * @phpstan-return Generator> */ public static function levelOrderGenerator(Node $node): Generator { diff --git a/src/Factory/BinaryTreeFactory.php b/src/Factory/BinaryTreeFactory.php index a57e58d..375c8c0 100644 --- a/src/Factory/BinaryTreeFactory.php +++ b/src/Factory/BinaryTreeFactory.php @@ -18,7 +18,8 @@ class BinaryTreeFactory /** * @TODO write example regarding expecting list format ! * - * @param list $list + * @param array $list โš  Must be a 0 indexed list, 0 to n consecutive indexes + * @phpstan-param list $list * @param TNodeCreator $nodeCreator * * @return null|Node|TNode diff --git a/tests/Technical/Algorithm/TraversalTestTrait.php b/tests/Technical/Algorithm/TraversalTestTrait.php index 1c6e019..ce74f0c 100644 --- a/tests/Technical/Algorithm/TraversalTestTrait.php +++ b/tests/Technical/Algorithm/TraversalTestTrait.php @@ -10,7 +10,8 @@ trait TraversalTestTrait { /** - * @param list $expected + * @param array $expected + * @phpstan-param list $expected * @param iterable $actual */ protected static function assertSameTreeNodeValues(array $expected, iterable $actual, Assert $assert): void @@ -24,7 +25,8 @@ protected static function assertSameTreeNodeValues(array $expected, iterable $ac } /** - * @param list> $expected + * @param array> $expected + * @phpstan-param array> $expected * @param iterable> $actual */ protected static function assertLevelOrderSameTreeNodeValues(array $expected, iterable $actual, Assert $assert): void From fd034a4b48c23b525fc8d34b77a0144931ddd6bb Mon Sep 17 00:00:00 2001 From: Yoanm <4410697+yoanm@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:00:53 +0200 Subject: [PATCH 3/5] Improve --- .../BinaryTree/RecursiveReversedTraversal.php | 33 +++----- .../BinaryTree/RecursiveTraversal.php | 48 ++++++------ .../BinaryTree/ReversedTraversal.php | 48 +++++------- src/Algorithm/BinaryTree/Traversal.php | 78 ++++++++----------- .../BinaryTree/NodeInterface.php | 9 +-- src/Factory/BinaryTreeFactory.php | 20 +++-- .../Algorithm/TraversalTestTrait.php | 7 +- .../Factory/BinaryTreeFactoryTest.php | 2 +- 8 files changed, 105 insertions(+), 140 deletions(-) diff --git a/src/Algorithm/BinaryTree/RecursiveReversedTraversal.php b/src/Algorithm/BinaryTree/RecursiveReversedTraversal.php index 80cd22d..1448455 100644 --- a/src/Algorithm/BinaryTree/RecursiveReversedTraversal.php +++ b/src/Algorithm/BinaryTree/RecursiveReversedTraversal.php @@ -24,51 +24,48 @@ * * * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\ReversedTraversal for iterative implementations. - * - * - * @template TNode of Node The actual Node class */ class RecursiveReversedTraversal { /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal::preOrderGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return array + * @phpstan-return list */ public static function preOrder(Node $node): array { // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + // โš  Do not preserve keys in order to always return a list ! return iterator_to_array(self::preOrderGenerator($node), false); } /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal::preOrderGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return array + * @phpstan-return list */ public static function inOrder(Node $node): array { // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + // โš  Do not preserve keys in order to always return a list ! return iterator_to_array(self::inOrderGenerator($node), false); } /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal::inOrderGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return array + * @phpstan-return list */ public static function postOrder(Node $node): array { // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + // โš  Do not preserve keys in order to always return a list ! return iterator_to_array(self::postOrderGenerator($node), false); } @@ -88,9 +85,7 @@ public static function postOrder(Node $node): array * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! * * - * @param TNode $node - * - * @return Generator + * @return Generator */ public static function preOrderGenerator(Node $node): Generator { @@ -120,9 +115,7 @@ public static function preOrderGenerator(Node $node): Generator * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! * * - * @param TNode $node - * - * @return Generator + * @return Generator */ public static function inOrderGenerator(Node $node): Generator { @@ -153,9 +146,7 @@ public static function inOrderGenerator(Node $node): Generator * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! * * - * @param TNode $node - * - * @return Generator + * @return Generator */ public static function postOrderGenerator(Node $node): Generator { diff --git a/src/Algorithm/BinaryTree/RecursiveTraversal.php b/src/Algorithm/BinaryTree/RecursiveTraversal.php index 0c7fbe4..bac2df0 100644 --- a/src/Algorithm/BinaryTree/RecursiveTraversal.php +++ b/src/Algorithm/BinaryTree/RecursiveTraversal.php @@ -14,51 +14,48 @@ * * * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal for iterative implementations. - * - * - * @template TNode of Node The actual Node class */ class RecursiveTraversal { /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::preOrderGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return array + * @phpstan-return list */ public static function preOrder(Node $node): array { // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + // โš  Do not preserve keys in order to always return a list ! return iterator_to_array(self::preOrderGenerator($node), false); } /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::preOrderGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return array + * @phpstan-return list */ public static function inOrder(Node $node): array { // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + // โš  Do not preserve keys in order to always return a list ! return iterator_to_array(self::inOrderGenerator($node), false); } /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::inOrderGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return array + * @phpstan-return list */ public static function postOrder(Node $node): array { // โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used ! + // โš  Do not preserve keys in order to always return a list ! return iterator_to_array(self::postOrderGenerator($node), false); } @@ -78,15 +75,20 @@ public static function postOrder(Node $node): array * * SC: ๐‘‚โŸฎ๐‘›โŸฏ - Due to the list storing every node for every level (see levelOrderHelper() function). * - * @return array> key is the level, value is the list of nodes for that level - * @phpstan-return list> + * + * @return array> key is the level, value is the list of nodes for that level + * @phpstan-return list> */ - public static function levelOrder(Node $root): array + public static function levelOrder(Node $node): array { $res = []; - self::levelOrderHelper($root, $res); + self::levelOrderHelper($node, $res); + /** + * No easy way to tell PHPStan that $res is a list in case level is not provided :/ + * @var list> $res + */ return $res; } @@ -104,9 +106,8 @@ public static function levelOrder(Node $root): array * * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! * - * @param TNode $node * - * @return Generator + * @return Generator */ public static function preOrderGenerator(Node $node): Generator { @@ -134,9 +135,8 @@ public static function preOrderGenerator(Node $node): Generator * * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! * - * @param TNode $node * - * @return Generator + * @return Generator */ public static function inOrderGenerator(Node $node): Generator { @@ -165,9 +165,8 @@ public static function inOrderGenerator(Node $node): Generator * * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances ! * - * @param TNode $node * - * @return Generator + * @return Generator */ public static function postOrderGenerator(Node $node): Generator { @@ -198,10 +197,9 @@ public static function postOrderGenerator(Node $node): Generator * * SC: ๐‘‚โŸฎhโŸฏ - Due to the stack trace (recursive calls) * - * @param TNode $node * - * @param array> $res key is the level, value is the list of nodes for that level - * @phpstan-param array> $res + * @param array> $res key is the level, value is the list of nodes for that level + * @phpstan-param array> $res */ public static function levelOrderHelper(Node $node, array &$res, int $level = 0): void { diff --git a/src/Algorithm/BinaryTree/ReversedTraversal.php b/src/Algorithm/BinaryTree/ReversedTraversal.php index 466e470..5d2a08f 100644 --- a/src/Algorithm/BinaryTree/ReversedTraversal.php +++ b/src/Algorithm/BinaryTree/ReversedTraversal.php @@ -24,62 +24,59 @@ * * * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal for recursive implementations. - * - * - * @template TNode of Node The actual Node class */ class ReversedTraversal { /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RevervedTraversal::preOrderGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return array + * @phpstan-return list */ public static function preOrder(Node $node): array { - return iterator_to_array(self::preOrderGenerator($node)); + // โš  Do not preserve keys in order to always return a list ! + return iterator_to_array(self::preOrderGenerator($node), false); } /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RevervedTraversal::inOrderGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return array + * @phpstan-return list */ public static function inOrder(Node $node): array { - return iterator_to_array(self::inOrderGenerator($node)); + // โš  Do not preserve keys in order to always return a list ! + return iterator_to_array(self::inOrderGenerator($node), false); } /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RevervedTraversal::postOrderGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return array + * @phpstan-return list */ public static function postOrder(Node $node): array { - return iterator_to_array(self::postOrderGenerator($node)); + // โš  Do not preserve keys in order to always return a list ! + return iterator_to_array(self::postOrderGenerator($node), false); } /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RevervedTraversal::BFSGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return array + * @phpstan-return list */ public static function BFS(Node $node): array { - return iterator_to_array(self::BFSGenerator($node)); + // โš  Do not preserve keys in order to always return a list ! + return iterator_to_array(self::BFSGenerator($node), false); } /** @@ -98,9 +95,7 @@ public static function BFS(Node $node): array * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes path up to the root node. * * - * @param TNode $node - * - * @return Generator + * @return Generator */ public static function preOrderGenerator(Node $node): Generator { @@ -138,8 +133,6 @@ public static function preOrderGenerator(Node $node): Generator * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes path up to the root node. * * - * @param TNode $node - * * @return Generator */ public static function inOrderGenerator(Node $node): Generator @@ -181,8 +174,6 @@ public static function inOrderGenerator(Node $node): Generator * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes. * * - * @param TNode $node - * * @return Generator */ public static function postOrderGenerator(Node $node): Generator @@ -231,12 +222,11 @@ public static function postOrderGenerator(Node $node): Generator * SC: ๐‘‚โŸฎ๐‘šโŸฏ - Due to the queue storing current level parent nodes * * - * @param TNode $node - * - * @return Generator + * @return Generator */ public static function BFSGenerator(Node $node): Generator { + /** @var SplQueue $queue */ $queue = new SplQueue(); $queue->enqueue($node); diff --git a/src/Algorithm/BinaryTree/Traversal.php b/src/Algorithm/BinaryTree/Traversal.php index d2f29bb..9581f27 100644 --- a/src/Algorithm/BinaryTree/Traversal.php +++ b/src/Algorithm/BinaryTree/Traversal.php @@ -14,80 +14,74 @@ * * * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal for recursive implementations. - * - * - * @template TNode of Node The actual Node class */ class Traversal { /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::preOrderGenerator() * - * @param TNode $node + * - * @return array - * @phpstan-return list + * @return Node[] + * @phpstan-return list */ public static function preOrder(Node $node): array { - return iterator_to_array(self::preOrderGenerator($node)); + // โš  Do not preserve keys in order to always return a list ! + return iterator_to_array(self::preOrderGenerator($node), false); } /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::inOrderGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return Node[] + * @phpstan-return list */ public static function inOrder(Node $node): array { - return iterator_to_array(self::inOrderGenerator($node)); + // โš  Do not preserve keys in order to always return a list ! + return iterator_to_array(self::inOrderGenerator($node), false); } /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::postOrderGenerator() * - * @param TNode $node * - * @return array - * @phpstan-return list + * @return Node[] + * @phpstan-return list */ public static function postOrder(Node $node): array { - return iterator_to_array(self::postOrderGenerator($node)); + // โš  Do not preserve keys in order to always return a list ! + return iterator_to_array(self::postOrderGenerator($node), false); } /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::BFSGenerator() * - * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::levelOrder() if node level must be known ! * - * @param TNode $node - * - * @return array - * @phpstan-return list + * @return Node[] + * @phpstan-return list */ public static function BFS(Node $node): array { - return iterator_to_array(self::BFSGenerator($node)); + // โš  Do not preserve keys in order to always return a list ! + return iterator_to_array(self::BFSGenerator($node), false); } /** * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::levelOrderGenerator() - * - * * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::BFS() if node level isn't useful. * - * @param TNode $node * - * @return array> key is the level, value is the list of nodes for that level - * @phpstan-return list> + * @return array> key is the level, value is the list of nodes for that level + * @phpstan-return list> */ public static function levelOrder(Node $node): array { - return iterator_to_array(self::levelOrderGenerator($node)); + // โš  Do not preserve keys in order to always return a list ! + return iterator_to_array(self::levelOrderGenerator($node), false); } /** @@ -106,17 +100,16 @@ public static function levelOrder(Node $node): array * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes path up to the root node. * * - * @param TNode $node - * - * @return Generator + * @return Generator */ public static function preOrderGenerator(Node $node): Generator { + /** @var SplStack $stack */ $stack = new SplStack(); $stack->push($node); + while (!$stack->isEmpty()) { - /** @var Node $currentNode */ $currentNode = $stack->pop(); yield $currentNode; @@ -145,14 +138,13 @@ public static function preOrderGenerator(Node $node): Generator * * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes path up to the root node. * - * - * @param TNode $node - * * @return Generator */ public static function inOrderGenerator(Node $node): Generator { + /** @var SplStack $stack */ $stack = new SplStack(); + $currentNode = $node; while (null !== $currentNode || !$stack->isEmpty()) { while (null !== $currentNode) { @@ -162,7 +154,6 @@ public static function inOrderGenerator(Node $node): Generator // Current node becomes the leftmost leaf found // (or the same current node in case it doesn't have left node!) - /** @var Node $currentNode */ $currentNode = $stack->pop(); yield $currentNode; @@ -188,12 +179,11 @@ public static function inOrderGenerator(Node $node): Generator * SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack storing parent nodes path up to the root node. * * - * @param TNode $node - * * @return Generator */ public static function postOrderGenerator(Node $node): Generator { + /** @var SplStack $stack */ $stack = new SplStack(); $currentNode = $node; @@ -206,7 +196,6 @@ public static function postOrderGenerator(Node $node): Generator $currentNode = $currentNode->left; } - /** @var Node $currentNode */ $currentNode = $stack->pop(); if (!$stack->isEmpty() && $currentNode->right === $stack->top()) { @@ -246,16 +235,16 @@ public static function postOrderGenerator(Node $node): Generator * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::BFSGenerator() if node level isn't useful. * * - * @param TNode $node - * - * @return Generator> key is the level, value is the list of nodes for that level - * @phpstan-return Generator> + * @return Generator> key is the level, value is the list of nodes for that level + * @phpstan-return Generator> */ public static function levelOrderGenerator(Node $node): Generator { + /** @var SplQueue $queue */ $queue = new SplQueue(); $queue->enqueue($node); + while (!$queue->isEmpty()) { $nodeList = []; $currentLevelNodeCounter = $queue->count(); @@ -296,12 +285,11 @@ public static function levelOrderGenerator(Node $node): Generator * @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal::levelOrderGenerator() if node level must be known ! * * - * @param TNode $node - * - * @return Generator + * @return Generator */ public static function BFSGenerator(Node $node): Generator { + /** @var SplQueue $queue */ $queue = new SplQueue(); $queue->enqueue($node); diff --git a/src/DataStructure/BinaryTree/NodeInterface.php b/src/DataStructure/BinaryTree/NodeInterface.php index e221a06..c061966 100644 --- a/src/DataStructure/BinaryTree/NodeInterface.php +++ b/src/DataStructure/BinaryTree/NodeInterface.php @@ -5,12 +5,11 @@ /** * Binary tree node interface. * - * - * @property null|NodeInterface $left - * @property null|NodeInterface $right + * @property ?NodeInterface $left + * @property ?NodeInterface $right */ interface NodeInterface { - // public null|NodeInterface $left {get; set;} // @TODO uncomment once php 8.4 minimum is required ! - // public null|NodeInterface $right {get; set;} // @TODO uncomment once php 8.4 minimum is required ! + // public ?NodeInterface $left {get; set;} // @TODO uncomment once php 8.4 minimum is required ! + // public ?NodeInterface $right {get; set;} // @TODO uncomment once php 8.4 minimum is required ! } diff --git a/src/Factory/BinaryTreeFactory.php b/src/Factory/BinaryTreeFactory.php index 375c8c0..b5315b3 100644 --- a/src/Factory/BinaryTreeFactory.php +++ b/src/Factory/BinaryTreeFactory.php @@ -7,24 +7,22 @@ use SplQueue; use Yoanm\CommonDSA\DataStructure\BinaryTree\NodeInterface as Node; -/** - * @template TNode of Node The actual Node class - * @template TValue of mixed The actual Node value type - * - * @phpstan-type TNodeCreator callable(TValue $v): TNode - */ class BinaryTreeFactory { /** * @TODO write example regarding expecting list format ! * - * @param array $list โš  Must be a 0 indexed list, 0 to n consecutive indexes + * @template TNode of Node The actual Node class + * @template TValue of mixed The actual Node value type + * + * + * @param mixed[] $list โš  Must be a 0 indexed list, 0 to n consecutive indexes * @phpstan-param list $list - * @param TNodeCreator $nodeCreator + * @phpstan-param callable(TValue $v): TNode $nodeCreator * - * @return null|Node|TNode + * @phpstan-return ?TNode */ - public static function fromLevelOrderList(array $list, callable $nodeCreator): null|Node + public static function fromLevelOrderList(array $list, callable $nodeCreator): ?Node { $tailIdx = count($list) - 1; if ($tailIdx < 0 || (0 === $tailIdx && null === $list[0])) { @@ -33,7 +31,7 @@ public static function fromLevelOrderList(array $list, callable $nodeCreator): n $queue = new SplQueue(); - $root = call_user_func($nodeCreator, $list[0]); + $root = call_user_func($nodeCreator, $list[0]); // @phpstan-ignore argument.type $queue->enqueue($root); $idx = 1; // Root value already managed, hence 1 rather 0 ! diff --git a/tests/Technical/Algorithm/TraversalTestTrait.php b/tests/Technical/Algorithm/TraversalTestTrait.php index ce74f0c..29f7b98 100644 --- a/tests/Technical/Algorithm/TraversalTestTrait.php +++ b/tests/Technical/Algorithm/TraversalTestTrait.php @@ -32,11 +32,12 @@ protected static function assertSameTreeNodeValues(array $expected, iterable $ac protected static function assertLevelOrderSameTreeNodeValues(array $expected, iterable $actual, Assert $assert): void { $newActual = []; - foreach ($actual as $key => $nodeList) { - $newActual[$key] = []; + foreach ($actual as $nodeList) { + $tmpList = []; foreach ($nodeList as $node) { - $newActual[$key][] = $node->val; + $tmpList[] = $node->val; } + $newActual[] = $tmpList; } $assert::assertSame($expected, $newActual); diff --git a/tests/Technical/Factory/BinaryTreeFactoryTest.php b/tests/Technical/Factory/BinaryTreeFactoryTest.php index 7448e26..477ecef 100644 --- a/tests/Technical/Factory/BinaryTreeFactoryTest.php +++ b/tests/Technical/Factory/BinaryTreeFactoryTest.php @@ -25,7 +25,7 @@ public function testFromLevelOrderList(array $data, array $expected): void $root = Factory::fromLevelOrderList($data, static fn(int $val) => new Node($val)); self::assertNotNull($root); - self::assertInstanceOf(Node::class, $root); + self::assertInstanceOf(Node::class, $root); // @phpstan-ignore staticMethod.alreadyNarrowedType self::assertLevelOrderSameTreeNodeValues($expected, Helper::levelOrder($root), $this); } From ee0261590fc02455d4f27e1e46a5b8291d02775f Mon Sep 17 00:00:00 2001 From: Yoanm <4410697+yoanm@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:11:03 +0200 Subject: [PATCH 4/5] Fix --- src/Algorithm/BinaryTree/RecursiveTraversal.php | 2 +- tests/Technical/Algorithm/TraversalTestTrait.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Algorithm/BinaryTree/RecursiveTraversal.php b/src/Algorithm/BinaryTree/RecursiveTraversal.php index bac2df0..773d58c 100644 --- a/src/Algorithm/BinaryTree/RecursiveTraversal.php +++ b/src/Algorithm/BinaryTree/RecursiveTraversal.php @@ -87,7 +87,7 @@ public static function levelOrder(Node $node): array /** * No easy way to tell PHPStan that $res is a list in case level is not provided :/ - * @var list> $res + * @phpstan-var list> $res */ return $res; } diff --git a/tests/Technical/Algorithm/TraversalTestTrait.php b/tests/Technical/Algorithm/TraversalTestTrait.php index 29f7b98..3ce58f9 100644 --- a/tests/Technical/Algorithm/TraversalTestTrait.php +++ b/tests/Technical/Algorithm/TraversalTestTrait.php @@ -5,7 +5,7 @@ namespace Tests\Technical\Algorithm; use PHPUnit\Framework\Assert; -use Yoanm\CommonDSA\DataStructure\NAryTree\Node; +use Yoanm\CommonDSA\DataStructure\BinaryTree\Node; trait TraversalTestTrait { From 1b5005eed829b3f95a46838bbdf578d0f636a8fe Mon Sep 17 00:00:00 2001 From: Yoanm <4410697+yoanm@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:19:30 +0200 Subject: [PATCH 5/5] Minor typo --- src/Algorithm/BinaryTree/RecursiveTraversal.php | 6 +++--- src/Algorithm/BinaryTree/ReversedTraversal.php | 6 +++--- src/Algorithm/BinaryTree/Traversal.php | 6 +++--- src/Factory/BinaryTreeFactory.php | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Algorithm/BinaryTree/RecursiveTraversal.php b/src/Algorithm/BinaryTree/RecursiveTraversal.php index 773d58c..114033b 100644 --- a/src/Algorithm/BinaryTree/RecursiveTraversal.php +++ b/src/Algorithm/BinaryTree/RecursiveTraversal.php @@ -93,7 +93,7 @@ public static function levelOrder(Node $node): array } /** - * Pre-order: N->L->R => Node, then Left children, then Right children + * Pre-order: N->L->R => Node, then Left child, then Right child * *
* ### Time/Space complexity @@ -122,7 +122,7 @@ public static function preOrderGenerator(Node $node): Generator } /** - * In-order (=DFS): L->N->R => Left children, then Node, then Right children + * In-order (=DFS): L->N->R => Left child, then Node, then Right child * *
* ### Time/Space complexity @@ -152,7 +152,7 @@ public static function inOrderGenerator(Node $node): Generator } /** - * Post-order: L->R->N => Left children, then Right children, then Node + * Post-order: L->R->N => Left child, then Right child, then Node * *
* ### Time/Space complexity diff --git a/src/Algorithm/BinaryTree/ReversedTraversal.php b/src/Algorithm/BinaryTree/ReversedTraversal.php index 5d2a08f..6428d18 100644 --- a/src/Algorithm/BinaryTree/ReversedTraversal.php +++ b/src/Algorithm/BinaryTree/ReversedTraversal.php @@ -99,11 +99,11 @@ public static function BFS(Node $node): array */ public static function preOrderGenerator(Node $node): Generator { + /** @var SplStack $stack */ $stack = new SplStack(); $stack->push($node); while (!$stack->isEmpty()) { - /** @var Node $currentNode */ $currentNode = $stack->pop(); yield $currentNode; @@ -137,6 +137,7 @@ public static function preOrderGenerator(Node $node): Generator */ public static function inOrderGenerator(Node $node): Generator { + /** @var SplStack $stack */ $stack = new SplStack(); $currentNode = $node; @@ -148,7 +149,6 @@ public static function inOrderGenerator(Node $node): Generator // Current node becomes the rightmost leaf found // (or the same current node in case it doesn't have right node!) - /** @var Node $currentNode */ $currentNode = $stack->pop(); yield $currentNode; @@ -178,6 +178,7 @@ public static function inOrderGenerator(Node $node): Generator */ public static function postOrderGenerator(Node $node): Generator { + /** @var SplStack $stack */ $stack = new SplStack(); $currentNode = $node; @@ -190,7 +191,6 @@ public static function postOrderGenerator(Node $node): Generator $currentNode = $currentNode->right; } - /** @var Node $currentNode */ $currentNode = $stack->pop(); if (!$stack->isEmpty() && $currentNode->left === $stack->top()) { diff --git a/src/Algorithm/BinaryTree/Traversal.php b/src/Algorithm/BinaryTree/Traversal.php index 9581f27..27e41c9 100644 --- a/src/Algorithm/BinaryTree/Traversal.php +++ b/src/Algorithm/BinaryTree/Traversal.php @@ -85,7 +85,7 @@ public static function levelOrder(Node $node): array } /** - * Pre-order: N->L->R => Node, then Left children, then Right children. + * Pre-order: N->L->R => Node, then Left child, then Right child. * * *
@@ -124,7 +124,7 @@ public static function preOrderGenerator(Node $node): Generator } /** - * In-order (=DFS): L->N->R => Left children, then Node, then Right children. + * In-order (=DFS): L->N->R => Left child, then Node, then Right child. * * *
@@ -164,7 +164,7 @@ public static function inOrderGenerator(Node $node): Generator } /** - * Post-order: L->R->N => Left children, then Right children, then Node. + * Post-order: L->R->N => Left child, then Right child, then Node. * * *
diff --git a/src/Factory/BinaryTreeFactory.php b/src/Factory/BinaryTreeFactory.php index b5315b3..1c4e296 100644 --- a/src/Factory/BinaryTreeFactory.php +++ b/src/Factory/BinaryTreeFactory.php @@ -29,6 +29,7 @@ public static function fromLevelOrderList(array $list, callable $nodeCreator): ? return null; } + /** @var SplQueue $queue */ $queue = new SplQueue(); $root = call_user_func($nodeCreator, $list[0]); // @phpstan-ignore argument.type @@ -36,7 +37,6 @@ public static function fromLevelOrderList(array $list, callable $nodeCreator): ? $idx = 1; // Root value already managed, hence 1 rather 0 ! while ($idx <= $tailIdx) { - /** @var Node|TNode $parentNode */ $parentNode = $queue->dequeue(); // 1. Manage left node value