php实现图的邻接表,关键路径,拓朴排序

<?php
    //调用
    require 'alGraph.php';
    $a = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j');

    $e = array('ab'=>'3', 'ac'=>'4', 'be'=>'6', 'bd'=>'5', 'cd'=>'8', 'cf'=>'7', 'de'=>'3', 'eg'=>'9', 'eh'=>'4', 'fh'=>'6', 'gj'=>'2', 'hi'=>'5', 'ij'=>'3'); 
    $test = new ALGraph($a, $e, 1, 1);
    print_r($test->criticalPath());
?>


alGraph.php
<?php
    /**
     * PHP实现图的邻接表
     * 
     * @author zhaojiangwei
     * @since  2011/11/1 16:00
     */

    //顶点类   
    class Vex{
        private $data;
        private $headLink;//第一条边
        private $enterLimit = 0;//顶点的入度

        public function Vex($data, $headLink = NULL){
            $this->data = $data;
            $this->headLink = $headLink;
        }

        //入度加+n
        public function enterLimitAdd($n = 1){
            $this->enterLimit += $n;
        }

        public function getEnterLimit(){
            return $this->enterLimit;
        }

        public function getData(){
            return $this->data;
        }

        public function getHeadLink(){
            return $this->headLink;
        }

        public function setHeadLink(& $link){
            $this->headLink = $link;
        }
    }
  
    //边类 
    class Arc{
        private $key;//该边顶点对应在顶点数组的下标
        private $weight;//路径长度
        private $next;//下一条边
         
        public function Arc($key, $weight = NULL, $next = NULL){
            $this->key= $key;
            $this->next = $next;
            $this->weight= $weight; 
        }

        public function getWeight(){
            return $this->weight;
        }

        public function getKey(){
            return $this->key;
        }

        public function getNext(){
            return $this->next;
        }

        public function setNext($next){
            $this->next = $next;
        }
    }
   
    //邻接表类 
    class ALGraph{
        private $vexsData;//外部输入的顶点数据,类似如array('a', 'b');
        private $vexs;//顶点数组
        private $arcData;//外部输入的边数据,如array('ab'=>'3'),键为边,值为权值
        private $excuteDfsResult;//深度优先遍历后的字符串结果
        private $hasList; //遍历时储存遍历过的结点下标
        private $queue; //广度优先遍历时的存储队列
        private $direct; //是否是有向图,0为无向,1为有向
        private $weight; //是否带权,0不带,1带
         
        //$direct:是否是有向图,0无向,1有向
        //$weight:是否带权,0不带,1带 
        public function ALGraph($vexsData, $arcData, $direct = 0, $weight = 0){
            $this->vexsData = $vexsData;
            $this->arcData = $arcData;
            $this->direct = $direct;
            $this->weight = $weight;

            $this->createHeadList();
            $this->createArc(); 
        }

        //创建顶点数组
        private function createHeadList(){
            foreach($this->vexsData as $value){
                $this->vexs[] = new Vex($value); 
            }
        }

        //创建边表
        private function createArc(){
            switch($this->weight){
                case '0'://不带权
                    $this->createNoWeightArc();
                    break;

                case '1'://带权
                    $this->createWeightArc();
                    break;
            }
        }

        //创建带权表
        private function createWeightArc(){
            foreach($this->arcData as $key=>$value){
                $edgeNode = str_split($key);
                $this->createConnect($edgeNode[0], $edgeNode[1], $value);
                
                if(!$this->direct){//有向图
                    $this->createConnect($edgeNode[1], $edgeNode[0], $value);
                }
            }
            
        }

        //创建不带权表
        private function createNoWeightArc(){
            foreach($this->arcData as $value){
                $str = str_split($value);
                
                $this->createConnect($str[0], $str[1]);
                if(!$this->direct){
                    $this->createConnect($str[1], $str[0]);
                }
            }
        }

        //依附于边的俩顶点建立关系
        //$weight: 权值,默认为无权值
        private function createConnect($first, $last, $weight = NULL){
            $lastTemp=& $this->vexs[$this->getVexByValue($last)];
            $lastTemp->enterLimitAdd(1);//入度+1

            $firstNode =& $this->vexs[$this->getVexByValue($first)];
            $lastNode = new Arc($this->getVexByValue($last), $weight);

            $lastNode->setNext($firstNode->getHeadLink());
            $firstNode->setHeadLink(& $lastNode);
        }

        //关键路径算法
        public function criticalPath(){
            $etvs = array();//最早开始时间 
            $ltvs = array();//最晚开始时间
            $stacks = array();//拓朴排序结点栈
            $result = array();//返回的结果

            foreach($this->vexs as $value){
                $etvs[$value->getData()] = 0;   
            }

            $this->expandSeq($etvs, $stacks);//拓朴排序,并填充$etvs与$stacks
            $temp = end($etvs);//结点栈的栈顶点,为数组的最后一个元素

            foreach($this->vexs as $value){
                $ltvs[$value->getData()] = $temp;                    
            }
            
            while($top = array_pop($stacks)){
                $pre = $top->getHeadLink();

                while($pre){
                    $tempNode = $this->vexs[$pre->getKey()];
                    if($ltvs[$top->getData()] > $ltvs[$tempNode->getData()] - $pre->getWeight()){
                        $ltvs[$top->getData()] = $ltvs[$tempNode->getData()] - $pre->getWeight();
                    }

                    $pre = $pre->getNext();
                }
            }
            
            foreach($this->vexs as $value){
                if($ltvs[$value->getData()] == $etvs[$value->getData()]){
                    $result[] = $value->getData();
                }
            }

            return $result;
        }

        //拓扑排序
        //$etv,关键路径,找最早开始时间,默认为不找
        //$stack排序后的顶点栈
        public function expandSeq(& $etv = FALSE, & $stacks){
            $zeroEnter = array();
            $result = array();
            
            foreach($this->vexs as $value){
                if($value->getEnterLimit() == 0){
                    $zeroEnter[] = $value;   
                }
            }
            
            while(!empty($zeroEnter)){
                $node = array_shift($zeroEnter);
                $result[] = $node->getData();
                array_push($stacks, $node);
                $pre = $node->getHeadLink();
                    
                while($pre){
                    $temp =& $this->vexs[$pre->getKey()];
                    $temp->enterLimitAdd(-1);

                    if($etv){
                        if($etv[$temp->getData()] < $etv[$node->getData()] + $pre->getWeight()){
                            $etv[$temp->getData()] = $etv[$node->getData()] + $pre->getWeight();
                        }
                    }

                    if($temp->getEnterLimit() == 0){
                        $zeroEnter[] = $temp; 
                    }

                    $pre = $pre->getNext();
                }
            }

            return $result;
        }

        //根据顶点的值返回顶点在顶点数组中的下标
        private function getVexByValue($value){
            foreach($this->vexs as $k=>$v){
                if($v->getData() == $value){
                    return $k; 
                }
            }
        }

        //广度优先遍历
        public function bfs(){
            $this->hasList = array();
            $this->queue = array();
            
            foreach($this->vexs as $key=>$value){
                if(!in_array($value->getData(), $this->hasList)){
                    $this->hasList[] = $value->getData();
                    $this->queue[] = $value->getHeadLink();
                     
                    while(!empty($this->queue)){
                        $node = array_shift($this->queue);
                        
                        while($node){
                            if(!in_array($this->vexs[$node->getKey()]->getData(), $this->hasList)){
                                $info = $this->vexs[$node->getKey()];
                                $this->hasList[] = $info->getData();
                                $this->queue[] = $info->getHeadLink();
                            }

                            $node = $node->getNext(); 
                        }
                    }
                }
            }

            return implode($this->hasList);
        }

        //深度优先遍历入口
        public function dfs(){
            $this->hasList = array();

            foreach($this->vexs as $key=>$value){
                if(!in_array($key, $this->hasList)){
                    $this->hasList[] = $key;
                    $this->excuteDfs($this->vexs[$key]->getHeadLink());
                }
            }

            foreach($this->hasList as $key=>$value){
                $this->hasList[$key] = $this->vexs[$value]->getData();   
            }

            return implode($this->hasList);
        }

        //执行深度遍历
        private function excuteDfs($arc){
            if(!$arc || in_array($arc->getKey(), $this->hasList)){
                return false;   
            }

            $this->hasList[] = $arc->getKey();
            $next = $this->vexs[$arc->getKey()]->getHeadLink();
        
            while($next){  
                $this->excuteDfs($next);
                $next = $next->getNext();
            }
        }

        public function getVexs(){
            return $this->vexs;
        }
    }

?>
posted @ 2011-11-02 18:30  黑暗遊侠  阅读(208)  评论(0编辑  收藏  举报