135 lines
4.3 KiB
PHP
135 lines
4.3 KiB
PHP
<?php
|
|
|
|
namespace Graphp\Algorithms\MinimumSpanningTree;
|
|
|
|
use Fhaculty\Graph\Exception\RuntimeException;
|
|
|
|
use Fhaculty\Graph\Exception\UnexpectedValueException;
|
|
|
|
use Fhaculty\Graph\Graph;
|
|
use Fhaculty\Graph\Edge\Directed as EdgeDirected;
|
|
use Fhaculty\Graph\Set\Edges;
|
|
use \SplPriorityQueue;
|
|
|
|
class Kruskal extends Base
|
|
{
|
|
/**
|
|
*
|
|
* @var Graph
|
|
*/
|
|
private $graph;
|
|
|
|
public function __construct(Graph $inputGraph)
|
|
{
|
|
$this->graph = $inputGraph;
|
|
}
|
|
|
|
protected function getGraph()
|
|
{
|
|
return $this->graph;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return Edges
|
|
*/
|
|
public function getEdges()
|
|
{
|
|
// Sortiere Kanten im Graphen
|
|
|
|
$sortedEdges = new SplPriorityQueue();
|
|
|
|
// For all edges
|
|
$this->addEdgesSorted($this->graph->getEdges(), $sortedEdges);
|
|
|
|
$returnEdges = array();
|
|
|
|
// next color to assign
|
|
$colorNext = 0;
|
|
// array(color1 => array(vid1, vid2, ...), color2=>...)
|
|
$colorVertices = array();
|
|
// array(vid1 => color1, vid2 => color1, ...)
|
|
$colorOfVertices = array();
|
|
|
|
// Füge billigste Kanten zu neuen Graphen hinzu und verschmelze teilgragen wenn es nötig ist (keine Kreise)
|
|
// solange ich mehr als einen Graphen habe mit weniger als n-1 kanten (bei n knoten im original)
|
|
foreach ($sortedEdges as $edge) {
|
|
/* @var $edge EdgeDirected */
|
|
// Gucke Kante an:
|
|
|
|
$vertices = $edge->getVertices()->getIds();
|
|
|
|
$aId = $vertices[0];
|
|
$bId = $vertices[1];
|
|
|
|
$aColor = isset($colorOfVertices[$aId]) ? $colorOfVertices[$aId] : NULL;
|
|
$bColor = isset($colorOfVertices[$bId]) ? $colorOfVertices[$bId] : NULL;
|
|
|
|
// 1. weder start noch end gehört zu einem graphen
|
|
// => neuer Graph mit kanten
|
|
if ($aColor === NULL && $bColor === NULL) {
|
|
$colorOfVertices[$aId] = $colorNext;
|
|
$colorOfVertices[$bId] = $colorNext;
|
|
|
|
$colorVertices[$colorNext] = array($aId, $bId);
|
|
|
|
++$colorNext;
|
|
|
|
// connect both vertices
|
|
$returnEdges []= $edge;
|
|
}
|
|
// 4. start xor end gehören zu einem graphen
|
|
// => erweitere diesesn Graphen
|
|
// Only b has color
|
|
else if ($aColor === NULL && $bColor !== NULL) {
|
|
// paint a in b's color
|
|
$colorOfVertices[$aId] = $bColor;
|
|
$colorVertices[$bColor][]=$aId;
|
|
|
|
$returnEdges []= $edge;
|
|
// Only a has color
|
|
} elseif ($aColor !== NULL && $bColor === NULL) {
|
|
// paint b in a's color
|
|
$colorOfVertices[$bId] = $aColor;
|
|
$colorVertices[$aColor][]=$bId;
|
|
|
|
$returnEdges []= $edge;
|
|
}
|
|
// 3. start und end gehören zu unterschiedlichen graphen
|
|
// => vereinigung
|
|
// Different color
|
|
else if ($aColor !== $bColor) {
|
|
$betterColor = $aColor;
|
|
$worseColor = $bColor;
|
|
|
|
// more vertices with color a => paint all in b in a's color
|
|
if (count($colorVertices[$bColor]) > count($colorVertices[$aColor])) {
|
|
$betterColor = $bColor;
|
|
$worseColor = $aColor;
|
|
}
|
|
|
|
// search all vertices with color b
|
|
foreach ($colorVertices[$worseColor] as $vid) {
|
|
$colorOfVertices[$vid] = $betterColor;
|
|
// repaint in a's color
|
|
$colorVertices[$betterColor][]=$vid;
|
|
}
|
|
// delete old color
|
|
unset($colorVertices[$worseColor]);
|
|
|
|
$returnEdges []= $edge;
|
|
}
|
|
// 2. start und end gehören zum gleichen graphen => zirkel
|
|
// => nichts machen
|
|
}
|
|
|
|
// definition of spanning tree: number of edges = number of vertices - 1
|
|
// above algorithm does not check isolated edges or may otherwise return multiple connected components => force check
|
|
if (count($returnEdges) !== (count($this->graph->getVertices()) - 1)) {
|
|
throw new UnexpectedValueException('Graph is not connected');
|
|
}
|
|
|
|
return new Edges($returnEdges);
|
|
}
|
|
}
|