Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

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

declare(strict_types=1);

namespace Yoanm\CommonDSA\Algorithm\BinaryTree;

use Generator;
use Yoanm\CommonDSA\DataStructure\BinaryTree\NodeInterface as Node;

/**
* Reversed binary tree traversal relying on recursive functions.<br>
* โš  Recursive function implies a growing stack trace, which has a limited size!<br>
* See {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\ReversedTraversal} for iterative implementations.
*
*
* <br>
* โ„น 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 !<br>
* Workaround:<br>
* 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.
*/
class RecursiveReversedTraversal
{
/**
* @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal::preOrderGenerator()
*
*
* @return array<Node>
* @phpstan-return list<Node>
*/
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()
*
*
* @return array<Node>
* @phpstan-return list<Node>
*/
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()
*
*
* @return array<Node>
* @phpstan-return list<Node>
*/
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);
}

/**
* Reversed Pre-order: N->R->L => Node, then Right children, then Left children
*
*
* <br>
* ### Time/Space complexity
* With:
* - ๐‘› the number of node in the tree
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
* โš  ๐˜ฉ = ๐‘› 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 !
*
*
* @return Generator<Node>
*/
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
*
*
* <br>
* ### Time/Space complexity
* With:
* - ๐‘› the number of node in the tree
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
* โš  ๐˜ฉ = ๐‘› 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 !
*
*
* @return Generator<Node>
*/
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
*
*
* <br>
* ### Time/Space complexity
* With:
* - ๐‘› the number of node in the tree
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
* โš  ๐˜ฉ = ๐‘› 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 !
*
*
* @return Generator<Node>
*/
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;
}
}
216 changes: 216 additions & 0 deletions src/Algorithm/BinaryTree/RecursiveTraversal.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
<?php

declare(strict_types=1);

namespace Yoanm\CommonDSA\Algorithm\BinaryTree;

use Generator;
use Yoanm\CommonDSA\DataStructure\BinaryTree\NodeInterface as Node;

/**
* Binary tree traversal relying on recursive functions.<br>
* โš  Recursive function implies a growing stack trace, which has a limited size!<br>
* See {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal} for iterative implementations.
*
*
* @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal for iterative implementations.
*/
class RecursiveTraversal
{
/**
* @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::preOrderGenerator()
*
*
* @return array<Node>
* @phpstan-return list<Node>
*/
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()
*
*
* @return array<Node>
* @phpstan-return list<Node>
*/
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()
*
*
* @return array<Node>
* @phpstan-return list<Node>
*/
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);
}

/**
* Level order (=BFS): Traverse tree level by level, from Left to Right
*
*
* <br>
* ๐Ÿ’ก Reverse the list for reversed level-order traversal !
* <br>
* <br>
* ### 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 array<int, array<Node>> key is the level, value is the list of nodes for that level
* @phpstan-return list<list<Node>>
*/
public static function levelOrder(Node $node): array
{
$res = [];

self::levelOrderHelper($node, $res);

/**
* No easy way to tell PHPStan that $res is a list in case level is not provided :/
* @phpstan-var list<list<Node>> $res
*/
return $res;
}

/**
* Pre-order: N->L->R => Node, then Left child, then Right child
*
* <br>
* ### Time/Space complexity
* With:
* - ๐‘› the number of node in the tree
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
* โš  ๐˜ฉ = ๐‘› 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 !
*
*
* @return Generator<Node>
*/
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 child, then Node, then Right child
*
* <br>
* ### Time/Space complexity
* With:
* - ๐‘› the number of node in the tree
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
* โš  ๐˜ฉ = ๐‘› 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 !
*
*
* @return Generator<Node>
*/
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 child, then Right child, then Node
*
* <br>
* ### Time/Space complexity
* With:
* - ๐‘› the number of node in the tree
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
* โš  ๐˜ฉ = ๐‘› 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 !
*
*
* @return Generator<Node>
*/
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
*
*
* <br>
* ๐Ÿ’ก Reverse the list for reversed level-order traversal !
* <br>
* <br>
* ### Time/Space complexity
* With:
* - ๐‘› the number of node in the tree
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
* โš  ๐˜ฉ = ๐‘› 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 array<int, array<Node>> $res key is the level, value is the list of nodes for that level
* @phpstan-param array<int, list<Node>> $res
*/
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);
}
}
}
Loading