数据结构之最小树生成(用php描述)
调用
try {
$graph = new DataStructureGraph();
$vertexArr = [
[1, 4, 1],
[1, 2, 2],
[4, 2, 3],
[1, 3, 4],
[3, 4, 2],
[3, 6, 5],
[4, 6, 8],
[6, 7, 1],
[4, 7, 4],
[7, 5, 6],
[2, 5, 10],
];
foreach ($vertexArr as $arr) {
$edge = new DataStructureEdge(new DataStructureVertex($arr[0]), new DataStructureVertex($arr[1]), $arr[2]);
$graph->addEdge($edge);
}
$tree = new DataStructureTree($graph);
$minimalSpanningTree = $tree->getMinimalSpanningTree();
/**@var DataStructureEdge $edge */
foreach ($minimalSpanningTree as $edge) {
echo implode(',', [$edge->getSource()->getNumber(), $edge->getTarget()->getNumber(), $edge->getWeight()]);
echo '<br/>';
}
} catch (Exception $e) {
var_dump($e->getMessage());
}
DataStructureTree类,对应的文件/var/www/test.com/DataStructure/Tree.php
<?php
namespace DataStructure;
class Tree
{
/**
* @var Graph $graph
*/
private $graph;
/**
* @var array $treeContainer
*/
private $treeContainer = [];
/**
* @var array $vertexes
*/
private $vertexes = [];
/**
* Tree constructor.
* @param Graph $graph
*/
public function __construct(Graph $graph)
{
$this->graph = $graph;
}
public function getMinimalSpanningTree()
{
$graphEdgeCount = $this->graph->getEdgeCount();
for ($i = 0; $i < $graphEdgeCount; $i++) {
$this->addMinimalEdge();
}
return $this->treeContainer;
}
/**
* 添加最小权值的边
*/
public function addMinimalEdge()
{
if (0 == count($this->treeContainer) && 0 < $this->graph->getEdgeCount()) {
$minWeightEdge = $this->graph->getMinWeightEdge();
$this->vertexes[] = $minWeightEdge->getSource();
$this->vertexes[] = $minWeightEdge->getTarget();
$this->treeContainer[] = $minWeightEdge;
} else {
$vertexConnectEdges = [];
foreach ($this->vertexes as $vertex) {
$vertexConnectEdges = array_merge($vertexConnectEdges, $this->getConnectEdgesByVertex($vertex));
}
$vertexConnectEdges = array_unique($vertexConnectEdges);
/**@var Edge $minWeightEdge */
$minWeightEdge = $this->getMinimalConnectEdge($vertexConnectEdges);
/**@var Edge $edge */
if ($minWeightEdge instanceof Edge) {
$this->vertexes[] = $minWeightEdge->getSource();
$this->vertexes[] = $minWeightEdge->getTarget();
$this->treeContainer[] = $minWeightEdge;
}
}
}
/**
* @param Edge $edge
* @return bool
*/
private function isHaveCircuit(Edge $edge)
{
$vertexNumbers = [];
/**@var Vertex $vertex */
foreach ($this->vertexes as $vertex) {
$vertexNumbers [] = $vertex->getNumber();
}
return in_array($edge->getSource()->getNumber(), $vertexNumbers)
&& in_array($edge->getTarget()->getNumber(), $vertexNumbers);
}
/**
* 获取权值最小的边
* @param array $edges
* @return Edge|null
*/
private function getMinimalConnectEdge(array $edges)
{
if (!is_array($edges)) {
return null;
}
//把树的边排除掉
/**@var Edge $edge */
foreach ($edges as &$connectEdge) {
foreach ($this->treeContainer as $edge) {
if ($connectEdge == $edge) {
$connectEdge = null;
}
}
}
$edges = array_filter($edges);
//按照权值从小到大升续排列,冒泡法
/**@var Edge $edgeOne */
/**@var Edge $edgeTwo */
foreach ($edges as &$edgeOne) {
foreach ($edges as &$edgeTwo) {
if ($edgeOne->getWeight() < $edgeTwo->getWeight()) {
$swap = $edgeTwo;
$edgeTwo = $edgeOne;
$edgeOne = $swap;
}
}
}
foreach ($edges as $edge) {
if (false == ($edge instanceof Edge)) {
continue;
}
if (false == $this->isHaveCircuit($edge)) {
return $edge;
}
}
return null;
}
/**
* 根据点获取相邻的所有边
* @param DataStructureVertex $vertex
* @return array
*/
private function getConnectEdgesByVertex(Vertex $vertex)
{
$connectEdges = [];
//获取点的相邻边
/**@var Edge $edge */
foreach ($this->graph->getGraph() as $edge) {
if ($edge->getSource() == $vertex || $edge->getTarget() == $vertex) {
$connectEdges[] = $edge;
}
}
return $connectEdges;
}
}
DataStructureGraph类,对应的文件/var/www/test.com/DataStructure/Graph.php
<?php
namespace DataStructure;
/**
* 图
*/
class Graph
{
private $edgeContainer = [];
private $vertexContainer = [];
/**
* @return array
*/
public function getVertexContainer()
{
return $this->vertexContainer;
}
/**
* @param Edge $edge
* @throws Exception
*/
public function addEdge(Edge $edge)
{
//遍历图,判断添加的边是否构成连通图
$edgeSourceValue = $edge->getSource()->getNumber();
$edgeTargetValue = $edge->getTarget()->getNumber();
$edgeWeight = $edge->getWeight();
if ($this->isEdgeExist($edge)) {
$remind = "添加边($edgeSourceValue,$edgeTargetValue,$edgeWeight)失败,边已经存在";
throw new Exception($remind);
}
if (false === $this->isVertexExist($edge->getSource())
&& false == $this->isVertexExist($edge->getTarget())
&& 0 < count($this->edgeContainer)
) {
$remind = "添加边($edgeSourceValue,$edgeTargetValue,$edgeWeight)失败,每次添加边后必须构成连通图";
throw new Exception($remind);
}
$this->vertexContainer[$edgeSourceValue] = $edge->getSource();
$this->vertexContainer[$edgeTargetValue] = $edge->getTarget();
array_push($this->edgeContainer, $edge);
}
/**
* @param Vertex $vertex
* @return bool
*/
public function isVertexExist(Vertex $vertex)
{
return isset($this->vertexContainer[$vertex->getNumber()]);
}
/**
* @param Edge $edge
* @return bool
*/
public function isEdgeExist(Edge $edge)
{
/**@var Edge $item */
foreach ($this->edgeContainer as $item) {
if ($edge->getSource()->getNumber() == $item->getSource()->getNumber()
&& $edge->getTarget()->getNumber() == $item->getTarget()->getNumber()
&& $edge->getWeight() == $item->getWeight()
) {
return true;
}
}
return false;
}
/**
* @return array
*/
public function getGraph()
{
return $this->edgeContainer;
}
/**
* @return int
*/
public function getEdgeCount()
{
return count($this->edgeContainer);
}
/**
* 获取最小权值的边
* @return Edge|null
*/
public function getMinWeightEdge()
{
if ($this->getEdgeCount() <= 0) {
return null;
}
$minWeightEdge = reset($this->edgeContainer);
/**@var Edge $edge */
foreach ($this->edgeContainer as $edge) {
if ($edge->getWeight() < $minWeightEdge->getWeight()) {
$minWeightEdge = $edge;
}
}
return $minWeightEdge;
}
}
DataStructureEdge类,对应的文件/var/www/test.com/DataStructure/Edge.php
<?php
namespace DataStructure;
/**
* 边
*/
class Edge
{
/**
* @var Vertex $source
*/
private $source;
/**
* @var Vertex $target
*/
private $target;
/**
* @var float $weight
*/
private $weight;
/**
* @return Vertex
*/
public function getSource()
{
return $this->source;
}
/**
* @return Vertex
*/
public function getTarget()
{
return $this->target;
}
/**
* @return float
*/
public function getWeight()
{
return $this->weight;
}
/**
* Edge constructor.
* @param Vertex $source
* @param Vertex $target
* @param float $weight
*/
public function __construct(Vertex $source, Vertex $target, $weight)
{
if (false === is_numeric($weight)) {
throw new Exception('权重必须是数字');
}
if ($source == $target) {
throw new Exception('起点和终点不能相同');
}
//始终把序号小的顶点作为无向图的起始,序号大的顶点作为无向图的终点
$min = min($source->getNumber(), $target->getNumber());
$max = max($source->getNumber(), $target->getNumber());
$source->setNumber($min);
$target->setNumber($max);
$this->source = $source;
$this->target = $target;
$this->weight = $weight;
}
public function __toString()
{
return $this->getSource()->getNumber() . ',' . $this->getTarget()->getNumber() . ',' . $this->getWeight();
}
}
DataStructureVertex类,对应的文件/var/www/test.com/DataStructure/Vertex.php
<?php
namespace DataStructure;
/**
* 点
*/
class Vertex
{
private $number;
/**
* @param mixed $number
*/
public function setNumber($number)
{
$this->number = $number;
}
/**
* @return mixed
*/
public function getNumber()
{
return $this->number;
}
/**
* Vertex constructor.
* @param $number
*/
public function __construct($number)
{
if (false === is_int($number) || 0 >= $number) {
throw new Exception('点必须是>1的正整数');
}
$this->number = $number;
}
}