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;
}
}

?>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值