ConnectedComponent::findConnectedComponents()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 15
rs 10
1
<?php
2
3
namespace Fisharebest\Algorithm;
4
5
/**
6
 * @author    Greg Roach <[email protected]>
7
 * @copyright (c) 2021 Greg Roach <[email protected]>
8
 * @license   GPL-3.0+
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program. If not, see <https://www.gnu.org/licenses>.
20
 */
21
22
/**
23
 * Class ConnectedComponent - Use a depth-first search to find connected
24
 * components of an undirected graph.
25
 */
26
class ConnectedComponent
27
{
28
    /** @var integer[][] The graph, where $graph[node1][node2]=cost */
29
    protected $graph;
30
31
    /** @var int[] The component number for each node */
32
    protected $nodes;
33
34
    /** @var int The next connected-component to find. */
35
    protected $component;
36
37
    /**
38
     * @param integer[][] $graph
39
     */
40
    public function __construct($graph)
41
    {
42
        $this->graph = $graph;
43
        $this->nodes = array_fill_keys(array_keys($graph), 0);
44
        $this->component = 0;
45
    }
46
47
    /**
48
     * An array of components (arrays).
49
     *
50
     * @return array
51
     */
52
    public function findConnectedComponents()
53
    {
54
        // Find the first unallocated node
55
        $node = array_search(0, $this->nodes, true);
56
57
        while ($node !== false) {
58
            // Find the next connected-component.
59
            $this->component++;
60
            $this->depthFirstSearch($node, $this->component);
61
62
            // Find the next unallocated node.
63
            $node = array_search(0, $this->nodes, true);
64
        }
65
66
        return $this->groupResults();
67
    }
68
69
    /**
70
     * Group the nodes by component.
71
     *
72
     * @return array
73
     */
74
    private function groupResults()
75
    {
76
        $result = array();
77
        foreach ($this->nodes as $node => $component) {
78
            if (array_key_exists($component, $result)) {
79
                $result[$component][] = $node;
80
            } else {
81
                $result[$component] = array($node);
82
            }
83
        }
84
85
        return $result;
86
    }
87
88
    /**
89
     * Find all nodes connected to $node and mark them as part of
90
     * component $component.
91
     *
92
     * @param $node
93
     * @param $component
94
     */
95
    private function depthFirstSearch($node, $component)
96
    {
97
        $this->nodes[$node] = $component;
98
99
        foreach (array_keys($this->graph[$node]) as $next) {
100
            if ($this->nodes[$next] === 0) {
101
                $this->depthFirstSearch($next, $component);
102
            }
103
        }
104
    }
105
}
106