数据结构之最小树生成(用php描述)

数据结构之最小树生成(用php描述)

调用

try {
    $graph = new \DataStructure\Graph();
    $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 \DataStructure\Edge(new \DataStructure\Vertex($arr[0]), new \DataStructure\Vertex($arr[1]), $arr[2]);
        $graph->addEdge($edge);
    }
    $tree = new \DataStructure\Tree($graph);
    $minimalSpanningTree = $tree->getMinimalSpanningTree();
    /**@var \DataStructure\Edge $edge */
    foreach ($minimalSpanningTree as $edge) {
        echo implode(',', [$edge->getSource()->getNumber(), $edge->getTarget()->getNumber(), $edge->getWeight()]);
        echo '<br/>';
    }
} catch (\Exception $e) {
    var_dump($e->getMessage());
}

DataStructure\Tree类,对应的文件/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 \DataStructure\Vertex $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;
    }
}

DataStructure\Graph类,对应的文件/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;
    }
}

DataStructure\Edge类,对应的文件/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();
    }

}

DataStructure\Vertex类,对应的文件/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;
    }

}
posted @ 2017-04-23 23:53  一叶(foolishnoob)  阅读(481)  评论(0编辑  收藏  举报