Skip to content

Commit 6024660

Browse files
PudottaPomminjiegillet
authored andcommitted
PHP Tree Traversal (#344)
* PHP Tree Traversal
1 parent 9040faf commit 6024660

File tree

2 files changed

+136
-1
lines changed

2 files changed

+136
-1
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
class Tree implements JsonSerializable
4+
{
5+
private $id;
6+
private $children = [];
7+
8+
public function __construct(int $id, array $children = [])
9+
{
10+
$this->id = $id;
11+
$this->children = $children;
12+
}
13+
14+
public function getId(): int { return $this->id; }
15+
16+
public function getChildren(): array { return $this->children; }
17+
18+
public function addChild(Tree $child): void { $this->children[] = $child; }
19+
20+
public function jsonSerialize(): array
21+
{
22+
return [
23+
'id' => $this->id,
24+
'children' => $this->children
25+
];
26+
}
27+
}
28+
29+
class TreeTraversal
30+
{
31+
public static function dfs_recursive(Tree $tree): void
32+
{
33+
if ($tree->getId()) echo $tree->getId() . PHP_EOL;
34+
foreach ($tree->getChildren() as $child) static::dfs_recursive($child);
35+
}
36+
37+
public static function dfs_recursive_postorder(Tree $tree): void
38+
{
39+
foreach ($tree->getChildren() as $child) static::dfs_recursive_postorder($child);
40+
echo $tree->getId() . PHP_EOL;
41+
}
42+
43+
public static function dfs_recursive_inorder_binary(Tree $tree): void
44+
{
45+
switch (count($tree->getChildren())) {
46+
case 2:
47+
static::dfs_recursive_inorder_binary($tree->getChildren()[0]);
48+
echo $tree->getId() . PHP_EOL;
49+
static::dfs_recursive_inorder_binary($tree->getChildren()[1]);
50+
break;
51+
case 1:
52+
static::dfs_recursive_inorder_binary($tree->getChildren()[0]);
53+
echo $tree->getId() . PHP_EOL;
54+
break;
55+
case 0:
56+
echo $tree->getId() . PHP_EOL;
57+
break;
58+
default:
59+
throw new InvalidArgumentException('Not a binary tree!');
60+
break;
61+
}
62+
}
63+
64+
public static function dfs_stack(Tree $tree): void
65+
{
66+
$stack = [$tree];
67+
$temp = null;
68+
69+
while (null !== ($temp = array_pop($stack))) {
70+
echo $temp->getId() . PHP_EOL;
71+
foreach ($temp->getChildren() as $child) $stack[] = $child;
72+
}
73+
}
74+
75+
public static function dfs_queue(Tree $tree): void
76+
{
77+
$stack = [$tree];
78+
$temp = null;
79+
80+
while (null !== ($temp = array_shift($stack))) {
81+
echo $temp->getId() . PHP_EOL;
82+
foreach ($temp->getChildren() as $child) $stack[] = $child;
83+
}
84+
}
85+
}
86+
87+
function generate_tree(int $num_of_rows, int $num_of_children, int $id = -1): Tree
88+
{
89+
if ($id === -1) $id = 1;
90+
$node = new Tree($id);
91+
92+
if ($num_of_rows > 1)
93+
for ($i = 0; $i < $num_of_children; $i++) {
94+
$child = generate_tree($num_of_rows - 1, $num_of_children, $id * 10 + $i + 1);
95+
$node->addChild($child);
96+
}
97+
98+
return $node;
99+
}
100+
101+
$node = generate_tree(3, 3);
102+
echo sprintf('TreeTraversal in JSON format: %s%s%s', PHP_EOL, json_encode($node), PHP_EOL);
103+
104+
echo 'DFS Recursive:' . PHP_EOL;
105+
TreeTraversal::dfs_recursive($node);
106+
107+
echo 'DFS Recursive Postorder:' . PHP_EOL;
108+
TreeTraversal::dfs_recursive_postorder($node);
109+
110+
echo 'DFS Stack:' . PHP_EOL;
111+
TreeTraversal::dfs_stack($node);
112+
113+
echo 'DFS Queue:' . PHP_EOL;
114+
TreeTraversal::dfs_queue($node);
115+
116+
// If you want try binary order non-binary tree
117+
// Comment generation of new tree bellow
118+
// If you do that, exception will be thrown
119+
$node = generate_tree(3, 2);
120+
echo 'DFS Recursive Inorder Binary:' . PHP_EOL;
121+
TreeTraversal::dfs_recursive_inorder_binary($node);

contents/tree_traversal/tree_traversal.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ This has not been implemented in your chosen language, so here is the Julia code
2828
[import:1-3, lang:"haskell"](code/haskell/TreeTraversal.hs)
2929
{% sample lang="swift"%}
3030
[import:1-9, lang:"swift"](code/swift/tree.swift)
31+
{% sample lang="php"%}
32+
[import:3-27, lang:"php"](code/php/tree_traversal.php)
3133
{% endmethod %}
3234

3335
Because of this, the most straightforward way to traverse the tree might be recursive. This naturally leads us to the Depth-First Search (DFS) method:
@@ -58,6 +60,8 @@ Because of this, the most straightforward way to traverse the tree might be recu
5860
[import:5-6, lang:"haskell"](code/haskell/TreeTraversal.hs)
5961
{% sample lang="swift"%}
6062
[import:24-30, lang:"swift"](code/swift/tree.swift)
63+
{% sample lang="php"%}
64+
[import:31-35, lang:"php"](code/php/tree_traversal.php)
6165
{% endmethod %}
6266

6367
At least to me, this makes a lot of sense. We fight recursion with recursion! First, we first output the node we are on and then we call `DFS_recursive(...)` on each of its children nodes. This method of tree traversal does what its name implies: it goes to the depths of the tree first before going through the rest of the branches. In this case, the ordering looks like:
@@ -96,6 +100,8 @@ Now, in this case the first element searched through is still the root of the tr
96100
[import:8-9, lang:"haskell"](code/haskell/TreeTraversal.hs)
97101
{% sample lang="swift"%}
98102
[import:32-38, lang:"swift"](code/swift/tree.swift)
103+
{% sample lang="php"%}
104+
[import:37-41, lang:"php"](code/php/tree_traversal.php)
99105
{% endmethod %}
100106

101107
<p>
@@ -129,6 +135,8 @@ In this case, the first node visited is at the bottom of the tree and moves up t
129135
[import:11-15, lang:"haskell"](code/haskell/TreeTraversal.hs)
130136
{% sample lang="swift"%}
131137
[import:40-53, lang:"swift"](code/swift/tree.swift)
138+
{% sample lang="php"%}
139+
[import:43-62, lang:"php"](code/php/tree_traversal.php)
132140
{% endmethod %}
133141

134142
<p>
@@ -173,6 +181,8 @@ This has not been implemented in your chosen language, so here is the Julia code
173181
[import:45-56, lang:"julia"](code/julia/Tree.jl)
174182
{% sample lang="swift"%}
175183
[import:55-67, lang:"swift"](code/swift/tree.swift)
184+
{% sample lang="php"%}
185+
[import:64-73, lang:"php"](code/php/tree_traversal.php)
176186
{% endmethod %}
177187

178188
All this said, there are a few details about DFS that might not be idea, depending on the situation. For example, if we use DFS on an incredibly long tree, we will spend a lot of time going further and further down a single branch without searching the rest of the data structure. In addition, it is not the natural way humans would order a tree if asked to number all the nodes from top to bottom. I would argue a more natural traversal order would look something like this:
@@ -197,7 +207,7 @@ And this is exactly what Breadth-First Search (BFS) does! On top of that, it can
197207
{% sample lang="js" %}
198208
[import:45-52, lang:"javascript"](code/javascript/tree.js)
199209
{% sample lang="py" %}
200-
[import:63-74, lang:"python"](code/python/Tree_example.py)
210+
[import:75-84, lang:"python"](code/python/Tree_example.py)
201211
{% sample lang="scratch" %}
202212
<p>
203213
<img class="center" src="code/scratch/bfs.svg" width="400" />
@@ -208,6 +218,8 @@ And this is exactly what Breadth-First Search (BFS) does! On top of that, it can
208218
[import:17-20, lang:"haskell"](code/haskell/TreeTraversal.hs)
209219
{% sample lang="swift"%}
210220
[import:69-81, lang:"swift"](code/swift/tree.swift)
221+
{% sample lang="php"%}
222+
[import:65-74, lang:"php"](code/php/tree_traversal.php)
211223
{% endmethod %}
212224

213225
## Example Code
@@ -245,6 +257,8 @@ The code snippets were taken from this [Scratch project](https://scratch.mit.edu
245257
[import, lang:"haskell"](code/haskell/TreeTraversal.hs)
246258
{% sample lang="swift"%}
247259
[import, lang:"swift"](code/swift/tree.swift)
260+
{% sample lang="php"%}
261+
[import, lang:"php"](code/php/tree_traversal.php)
248262
{% endmethod %}
249263

250264

0 commit comments

Comments
 (0)