Skip to content

Commit 1d23cf3

Browse files
authored
Add binary tree traversal, data-structure and factory (#6)
1 parent c218575 commit 1d23cf3

File tree

15 files changed

+1686
-0
lines changed

15 files changed

+1686
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yoanm\CommonDSA\Algorithm\BinaryTree;
6+
7+
use Generator;
8+
use Yoanm\CommonDSA\DataStructure\BinaryTree\NodeInterface as Node;
9+
10+
/**
11+
* Reversed binary tree traversal relying on recursive functions.<br>
12+
* โš  Recursive function implies a growing stack trace, which has a limited size!<br>
13+
* See {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\ReversedTraversal} for iterative implementations.
14+
*
15+
*
16+
* <br>
17+
* โ„น Reversed level-order is not implemented as it implies to keep track of and look at each and every node in
18+
* order to detect the last level => Time and space complexity will be ๐‘‚โŸฎ๐‘›โŸฏ in any cases !<br>
19+
* Workaround:<br>
20+
* 1. Fetch the result of {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::levelOrder}
21+
* or {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::levelOrderGenerator}
22+
* 2. Either start from the end or reverse the whole list (the latter implies
23+
* an additional ๐‘‚โŸฎ๐‘›โŸฏ time complexity though !)
24+
*
25+
*
26+
* @see \Yoanm\CommonDSA\Algorithm\BinaryTree\ReversedTraversal for iterative implementations.
27+
*/
28+
class RecursiveReversedTraversal
29+
{
30+
/**
31+
* @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal::preOrderGenerator()
32+
*
33+
*
34+
* @return array<Node>
35+
* @phpstan-return list<Node>
36+
*/
37+
public static function preOrder(Node $node): array
38+
{
39+
// โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used !
40+
// โš  Do not preserve keys in order to always return a list !
41+
return iterator_to_array(self::preOrderGenerator($node), false);
42+
}
43+
44+
/**
45+
* @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal::preOrderGenerator()
46+
*
47+
*
48+
* @return array<Node>
49+
* @phpstan-return list<Node>
50+
*/
51+
public static function inOrder(Node $node): array
52+
{
53+
// โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used !
54+
// โš  Do not preserve keys in order to always return a list !
55+
return iterator_to_array(self::inOrderGenerator($node), false);
56+
}
57+
58+
/**
59+
* @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveReversedTraversal::inOrderGenerator()
60+
*
61+
*
62+
* @return array<Node>
63+
* @phpstan-return list<Node>
64+
*/
65+
public static function postOrder(Node $node): array
66+
{
67+
// โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used !
68+
// โš  Do not preserve keys in order to always return a list !
69+
return iterator_to_array(self::postOrderGenerator($node), false);
70+
}
71+
72+
/**
73+
* Reversed Pre-order: N->R->L => Node, then Right children, then Left children
74+
*
75+
*
76+
* <br>
77+
* ### Time/Space complexity
78+
* With:
79+
* - ๐‘› the number of node in the tree
80+
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
81+
* โš  ๐˜ฉ = ๐‘› if every node has only a right child (skewed tree)
82+
*
83+
* TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time
84+
*
85+
* SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances !
86+
*
87+
*
88+
* @return Generator<Node>
89+
*/
90+
public static function preOrderGenerator(Node $node): Generator
91+
{
92+
yield $node;
93+
94+
if (null !== $node->right) {
95+
yield from self::preOrderGenerator($node->right);
96+
}
97+
if (null !== $node->left) {
98+
yield from self::preOrderGenerator($node->left);
99+
}
100+
}
101+
102+
/**
103+
* Reversed In-order (=Reversed DFS): R->N->L => Right children, then Node, then Left children
104+
*
105+
*
106+
* <br>
107+
* ### Time/Space complexity
108+
* With:
109+
* - ๐‘› the number of node in the tree
110+
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
111+
* โš  ๐˜ฉ = ๐‘› if every node has only a right child (skewed tree)
112+
*
113+
* TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time
114+
*
115+
* SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances !
116+
*
117+
*
118+
* @return Generator<Node>
119+
*/
120+
public static function inOrderGenerator(Node $node): Generator
121+
{
122+
if (null !== $node->right) {
123+
yield from self::inOrderGenerator($node->right);
124+
}
125+
126+
yield $node;
127+
128+
if (null !== $node->left) {
129+
yield from self::inOrderGenerator($node->left);
130+
}
131+
}
132+
133+
/**
134+
* Reversed Post-order: R->L->N => Right children, then Left children, then Node
135+
*
136+
*
137+
* <br>
138+
* ### Time/Space complexity
139+
* With:
140+
* - ๐‘› the number of node in the tree
141+
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
142+
* โš  ๐˜ฉ = ๐‘› if every node has only a right child (skewed tree)
143+
*
144+
* TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time
145+
*
146+
* SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances !
147+
*
148+
*
149+
* @return Generator<Node>
150+
*/
151+
public static function postOrderGenerator(Node $node): Generator
152+
{
153+
if (null !== $node->right) {
154+
yield from self::postOrderGenerator($node->right);
155+
}
156+
if (null !== $node->left) {
157+
yield from self::postOrderGenerator($node->left);
158+
}
159+
yield $node;
160+
}
161+
}
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yoanm\CommonDSA\Algorithm\BinaryTree;
6+
7+
use Generator;
8+
use Yoanm\CommonDSA\DataStructure\BinaryTree\NodeInterface as Node;
9+
10+
/**
11+
* Binary tree traversal relying on recursive functions.<br>
12+
* โš  Recursive function implies a growing stack trace, which has a limited size!<br>
13+
* See {@see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal} for iterative implementations.
14+
*
15+
*
16+
* @see \Yoanm\CommonDSA\Algorithm\BinaryTree\Traversal for iterative implementations.
17+
*/
18+
class RecursiveTraversal
19+
{
20+
/**
21+
* @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::preOrderGenerator()
22+
*
23+
*
24+
* @return array<Node>
25+
* @phpstan-return list<Node>
26+
*/
27+
public static function preOrder(Node $node): array
28+
{
29+
// โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used !
30+
// โš  Do not preserve keys in order to always return a list !
31+
return iterator_to_array(self::preOrderGenerator($node), false);
32+
}
33+
34+
/**
35+
* @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::preOrderGenerator()
36+
*
37+
*
38+
* @return array<Node>
39+
* @phpstan-return list<Node>
40+
*/
41+
public static function inOrder(Node $node): array
42+
{
43+
// โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used !
44+
// โš  Do not preserve keys in order to always return a list !
45+
return iterator_to_array(self::inOrderGenerator($node), false);
46+
}
47+
48+
/**
49+
* @see \Yoanm\CommonDSA\Algorithm\BinaryTree\RecursiveTraversal::inOrderGenerator()
50+
*
51+
*
52+
* @return array<Node>
53+
* @phpstan-return list<Node>
54+
*/
55+
public static function postOrder(Node $node): array
56+
{
57+
// โš  Do not preserve keys otherwise there is conflicting keys in case "yield from" is used !
58+
// โš  Do not preserve keys in order to always return a list !
59+
return iterator_to_array(self::postOrderGenerator($node), false);
60+
}
61+
62+
/**
63+
* Level order (=BFS): Traverse tree level by level, from Left to Right
64+
*
65+
*
66+
* <br>
67+
* ๐Ÿ’ก Reverse the list for reversed level-order traversal !
68+
* <br>
69+
* <br>
70+
* ### Time/Space complexity
71+
* With:
72+
* - ๐‘› the number of node in the tree
73+
*
74+
* TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time
75+
*
76+
* SC: ๐‘‚โŸฎ๐‘›โŸฏ - Due to the list storing every node for every level (see levelOrderHelper() function).
77+
*
78+
*
79+
* @return array<int, array<Node>> key is the level, value is the list of nodes for that level
80+
* @phpstan-return list<list<Node>>
81+
*/
82+
public static function levelOrder(Node $node): array
83+
{
84+
$res = [];
85+
86+
self::levelOrderHelper($node, $res);
87+
88+
/**
89+
* No easy way to tell PHPStan that $res is a list in case level is not provided :/
90+
* @phpstan-var list<list<Node>> $res
91+
*/
92+
return $res;
93+
}
94+
95+
/**
96+
* Pre-order: N->L->R => Node, then Left child, then Right child
97+
*
98+
* <br>
99+
* ### Time/Space complexity
100+
* With:
101+
* - ๐‘› the number of node in the tree
102+
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
103+
* โš  ๐˜ฉ = ๐‘› if every node has only a left child (skewed tree)
104+
*
105+
* TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time
106+
*
107+
* SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances !
108+
*
109+
*
110+
* @return Generator<Node>
111+
*/
112+
public static function preOrderGenerator(Node $node): Generator
113+
{
114+
yield $node;
115+
116+
if (null !== $node->left) {
117+
yield from self::preOrderGenerator($node->left);
118+
}
119+
if (null !== $node->right) {
120+
yield from self::preOrderGenerator($node->right);
121+
}
122+
}
123+
124+
/**
125+
* In-order (=DFS): L->N->R => Left child, then Node, then Right child
126+
*
127+
* <br>
128+
* ### Time/Space complexity
129+
* With:
130+
* - ๐‘› the number of node in the tree
131+
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
132+
* โš  ๐˜ฉ = ๐‘› if every node has only a left child (skewed tree)
133+
*
134+
* TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time
135+
*
136+
* SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances !
137+
*
138+
*
139+
* @return Generator<Node>
140+
*/
141+
public static function inOrderGenerator(Node $node): Generator
142+
{
143+
if (null !== $node->left) {
144+
yield from self::inOrderGenerator($node->left);
145+
}
146+
147+
yield $node;
148+
149+
if (null !== $node->right) {
150+
yield from self::inOrderGenerator($node->right);
151+
}
152+
}
153+
154+
/**
155+
* Post-order: L->R->N => Left child, then Right child, then Node
156+
*
157+
* <br>
158+
* ### Time/Space complexity
159+
* With:
160+
* - ๐‘› the number of node in the tree
161+
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
162+
* โš  ๐˜ฉ = ๐‘› if every node has only a left child (skewed tree)
163+
*
164+
* TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time
165+
*
166+
* SC: ๐‘‚โŸฎ๐˜ฉโŸฏ - Due to the stack trace (recursive calls) + extra space for inner Generator class instances !
167+
*
168+
*
169+
* @return Generator<Node>
170+
*/
171+
public static function postOrderGenerator(Node $node): Generator
172+
{
173+
if (null !== $node->left) {
174+
yield from self::postOrderGenerator($node->left);
175+
}
176+
if (null !== $node->right) {
177+
yield from self::postOrderGenerator($node->right);
178+
}
179+
yield $node;
180+
}
181+
182+
/**
183+
* Level order (=BFS): Traverse tree level by level, from Left to Right
184+
*
185+
*
186+
* <br>
187+
* ๐Ÿ’ก Reverse the list for reversed level-order traversal !
188+
* <br>
189+
* <br>
190+
* ### Time/Space complexity
191+
* With:
192+
* - ๐‘› the number of node in the tree
193+
* - ๐˜ฉ the tree height, usually ใ’(๐‘›) (=balanced tree).<br>
194+
* โš  ๐˜ฉ = ๐‘› if every node has only a left child (skewed tree)
195+
*
196+
* TC: ๐‘‚โŸฎ๐‘›โŸฏ - Each node will be visited only one time
197+
*
198+
* SC: ๐‘‚โŸฎhโŸฏ - Due to the stack trace (recursive calls)
199+
*
200+
*
201+
* @param array<int, array<Node>> $res key is the level, value is the list of nodes for that level
202+
* @phpstan-param array<int, list<Node>> $res
203+
*/
204+
public static function levelOrderHelper(Node $node, array &$res, int $level = 0): void
205+
{
206+
$res[$level] ??= []; // In case current level hasn't been seen/managed yet
207+
208+
$res[$level][] = $node;
209+
if (null !== $node->left) {
210+
self::levelOrderHelper($node->left, $res, $level + 1);
211+
}
212+
if (null !== $node->right) {
213+
self::levelOrderHelper($node->right, $res, $level + 1);
214+
}
215+
}
216+
}

0 commit comments

Comments
ย (0)