zoukankan      html  css  js  c++  java
  • 数据结构之最小树生成(用php描述)

    数据结构之最小树生成(用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;
        }
    
    }
    
  • 相关阅读:
    MySQL主库异常,从库手动切换为主库方案
    快速搭建应用服务日志收集系统(Filebeat + ElasticSearch + kibana)
    CentOS7设置DNS服务器
    nginx/iptables动态IP黑白名单实现方案
    Python批量复制和重命名文件
    centos 7 配置php运行环境 (新)
    配置Nginx和php-fpm用Sock套接字连接时,找不到php-fpm.sock的原因
    php-fpm nginx 9000端口
    nginx与php-fpm通信的两种方式
    centos 7.2 常用命令useradd的使用
  • 原文地址:https://www.cnblogs.com/foolishnoob/p/6754738.html
Copyright © 2011-2022 走看看