-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathConnectedComponents.php
More file actions
149 lines (129 loc) · 4.74 KB
/
ConnectedComponents.php
File metadata and controls
149 lines (129 loc) · 4.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<?php
namespace Graphp\Algorithms;
use Graphp\Algorithms\Search\BreadthFirst as SearchBreadthFirst;
use Graphp\Graph\Exception\InvalidArgumentException;
use Graphp\Graph\Exception\UnderflowException;
use Graphp\Graph\Graph;
use Graphp\Graph\Vertex;
/**
* Algorithm for working with connected components
*
* @link http://en.wikipedia.org/wiki/Connected_component_%28graph_theory%29
* @link http://mathworld.wolfram.com/ConnectedGraph.html
* @link http://math.stackexchange.com/questions/50551/is-the-empty-graph-connected
*/
class ConnectedComponents extends BaseGraph
{
/**
* create subgraph with all vertices connected to given vertex (i.e. the connected component of ths given vertex)
*
* @param Vertex $vertex
* @return Graph
* @throws InvalidArgumentException if given vertex is not from same graph
* @uses AlgorithmSearchBreadthFirst::getVertices()
* @uses Graph::createGraphCloneVertices()
*/
public function createGraphComponentVertex(Vertex $vertex)
{
if ($vertex->getGraph() !== $this->graph) {
throw new InvalidArgumentException('This graph does not contain the given vertex');
}
return $this->graph->createGraphCloneVertices($this->createSearch($vertex)->getVertices());
}
/**
*
* @param Vertex $vertex
* @return SearchBreadthFirst
*/
private function createSearch(Vertex $vertex)
{
$alg = new SearchBreadthFirst($vertex);
// follow into both directions (loosely connected)
return $alg->setDirection(SearchBreadthFirst::DIRECTION_BOTH);
}
/**
* check whether this graph consists of only a single component
*
* If a Graph consists of only a single component, it is said to be a
* connected Graph, otherwise it's called a disconnected Graph.
*
* This method returns exactly the same result as checking
* <pre>($this->getNumberOfComponents() === 1)</pre>. However, using this
* method is faster than calling getNumberOfComponents(), as it only has to
* count all vertices in one component to see if the graph consists of only
* a single component.
*
* As such, a null Graph (a Graph with no vertices) is not considered
* connected here.
*
* @return bool
* @see self::getNumberOfComponents()
*/
public function isSingle()
{
try {
$vertex = $this->graph->getVertices()->getVertexFirst();
} catch (UnderflowException $e) {
// no first vertex => empty graph => has zero components
return false;
}
$alg = $this->createSearch($vertex);
return (\count($this->graph->getVertices()) === \count($alg->getVertices()));
}
/**
* count number of connected components
*
* A null Graph (a Graph with no vertices) will return 0 components.
*
* @return int number of components
* @uses Graph::getVertices()
* @uses AlgorithmSearchBreadthFirst::getVertices()
*/
public function getNumberOfComponents()
{
$visitedVertices = array();
$components = 0;
// for each vertices
foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) {
// did I visit this vertex before?
if (!isset($visitedVertices[$vid])) {
// get all vertices of this component
$newVertices = $this->createSearch($vertex)->getVertices()->getIds();
++$components;
// mark the vertices of this component as visited
foreach ($newVertices as $vid) {
$visitedVertices[$vid] = true;
}
}
}
// return number of components
return $components;
}
/**
* separate input graph into separate independent and unconnected graphs
*
* @return Graph[]
* @uses Graph::getVertices()
* @uses AlgorithmSearchBreadthFirst::getVertices()
*/
public function createGraphsComponents()
{
$visitedVertices = array();
$graphs = array();
// for each vertices
foreach ($this->graph->getVertices()->getMap() as $vid => $vertex) {
// did I visit this vertex before?
if (!isset($visitedVertices[$vid])) {
$alg = $this->createSearch($vertex);
// get all vertices of this component
$newVertices = $alg->getVertices();
// mark the vertices of this component as visited
foreach ($newVertices->getIds() as $vid) {
$visitedVertices[$vid] = true;
}
$graphs[] = $this->graph->createGraphCloneVertices($newVertices);
}
}
return $graphs;
}
}