started graph implementation

This commit is contained in:
Dennis Eichhorn 2020-01-19 18:53:41 +01:00
parent 5d43137f3c
commit 8c0905c39b
3 changed files with 228 additions and 170 deletions

View File

@ -137,4 +137,19 @@ class Edge
{
return $this->isDirected;
}
/**
* Compare edge weights
*
* @param Edge $e1 Edge 1
* @param Edge $e2 Edge 2
*
* @return int
*
* @since 1.0.0
*/
public static function compare(Edge $e1, Edge $e2) : int
{
return $e1->getWeight() <=> $e2->getWeight();
}
}

View File

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace phpOMS\Stdlib\Graph;
/**
* Tree class.
* Graph class.
*
* @package phpOMS\Stdlib\Graph
* @license OMS License 1.0
@ -27,21 +27,13 @@ class Graph
/**
* Nodes.
*
* @var array
* @var Node[]
* @since 1.0.0
*/
protected $nodes = [];
/**
* Edges.
*
* @var array
* @since 1.0.0
*/
protected $edges = [];
/**
* Add node to graph.
* Set node to graph.
*
* @param Node $node Graph node
*
@ -49,76 +41,9 @@ class Graph
*
* @since 1.0.0
*/
public function addNode(Node $node) : self
public function setNode(Node $node) : self
{
$this->nodes[] = $node;
return $this;
}
/**
* Add node to graph.
*
* @param Node $relative Relative graph node
* @param Node $node Graph node
*
* @return Graph
*
* @since 1.0.0
*/
public function addNodeRelative(Node $relative, Node $node) : self
{
$this->edges[] = new Edge($relative, $node);
return $this;
}
/**
* Set node in graph.
*
* @param mixed $key Key of node
* @param Node $node Graph node
*
* @return Graph
*
* @since 1.0.0
*/
public function setNode($key, Node $node) : self
{
$this->nodes[$key] = $node;
return $this;
}
/**
* Add edge to graph.
*
* @param Edge $edge Graph edge
*
* @return Graph
*
* @since 1.0.0
*/
public function addEdge(Edge $edge) : self
{
$this->edges[] = $edge;
return $this;
}
/**
* Set edge in graph.
*
* @param mixed $key Edge key
* @param Edge $edge Edge to set
*
* @return Graph
*
* @since 1.0.0
*/
public function setEdge($key, Edge $edge) /* : void */
{
$this->edges[$key] = $edge;
$this->nodes[$node->getId()] = $node;
return $this;
}
@ -149,103 +74,32 @@ class Graph
return $this->nodes;
}
/**
* Get graph edge.
*
* @param mixed $key Edge key
*
* @return null|Edge
*
* @since 1.0.0
*/
public function getEdge($key) : ?Edge
{
return $this->edges[$key] ?? null;
}
/**
* Get graph edges
*
* @return Node[]
*
* @since 1.0.0
*/
public function getEdges() : array
{
return $this->edges;
}
/**
* Get all edges of a node
*
* @param mixed $node Node
*
* @return Edge[]
*
* @since 1.0.0
*/
public function getEdgesOfNode($node) : array
public function getEdges() : array
{
if (!($node instanceof Node)) {
$node = $this->getNode($node);
}
$edges = [];
foreach ($this->edges as $edge) {
$nodes = $edge->getNodes();
if ($nodes[0] === $node || $nodes[1] === $node) {
$edges[] = $edge;
foreach ($this->nodes as $node) {
$nodeEdges = $node->getEdges();
foreach ($nodeEdges as $edge) {
if (!isset($edges[$edge->getNode1()->getId() . ':' . $edge->getNode2()->getId()])
&& !isset($edges[$edge->getNode2()->getId() . ':' . $edge->getNode1()->getId()])
) {
$edges[$edge->getNode1()->getId() . ':' . $edge->getNode2()->getId()] = $edge;
}
}
}
return $edges;
}
/**
* Get all node neighbors.
*
* @param mixed $node Graph node
*
* @return Node[]
*
* @since 1.0.0
*/
public function getNeighbors($node) : array
{
if (!($node instanceof Node)) {
$node = $this->getNode($node);
}
$edges = $this->getEdgesOfNode($node);
$neighbors = [];
foreach ($edges as $edge) {
$nodes = $edge->getNodes();
if ($nodes[0] !== $node && $nodes[0] !== null) {
$neighbors[] = $nodes[0];
} elseif ($nodes[1] !== $node && $nodes[0] !== null) {
$neighbors[] = $nodes[1];
}
}
return $neighbors;
}
/**
* Get graph dimension.
*
* @return int
*
* @since 1.0.0
*/
public function getDimension() : int
{
// todo: implement
return 0;
}
/**
* Get all bridges.
*
@ -255,8 +109,72 @@ class Graph
*/
public function getBridges() : array
{
// todo: implement
return [];
$visited = [];
$parent = [];
$discovery = [];
$low = [];
$index = 0;
$bridges = [];
foreach ($this->nodes as $i => $node) {
if (!isset($visited[$i]) || $visited[$i] === false) {
$this->bridgesDepthFirstSearch($node, $visited, $discovery, $low, $parent, $index, $bridges);
}
}
return $bridges;
}
/**
* Fill bridge array
*
* @param Node $node Node to check bridge for
* @param bool[] $visited Visited nodes
* @param int[] $discovery Discovered
* @param int[] $low Lowest preorder of any vertex connected to ?
* @param Node[] $parent Parent node
* @param int $index Node index
* @param Edge[] $bridges Edges which represent bridges
*
* @return void
*
* @since 1.0.0
*/
private function bridgesDepthFirstSearch(
Node $node,
array &$visited,
array &$discovery,
array &$low,
array &$parent,
int &$index,
array &$bridges
) : void {
$id = $node->getId();
$visited[$id] = true;
++$index;
$discovery[$id] = $index;
$low[$id] = $index;
$edges = $this->nodes[$id]->getEdges();
foreach ($edges as $edge) {
$neighbor = !$edge->getNode1()->isEqual($node) ? $edge->getNode1() : $edge->getNode2();
if (!isset($visited[$neighbor->getId()]) || !$visited[$neighbor->getId()]) {
$parent[$neighbor->getId()] = $node;
$this->bridgesDepthFirstSearch($neighbor, $visited, $discovery, $low, $parent, $index, $bridges);
$low[$id] = \min($low[$id], $low[$neighbor->getId()]);
if ($low[$neighbor->getId()] > $discovery[$id]) {
$bridges[] = $edge;
}
} elseif (isset($parent[$id]) && !$neighbor->isEqual($parent[$id])) {
$low[$id] = \min($low[$id], $discovery[$neighbor->getId()]);
}
}
}
/**
@ -268,7 +186,6 @@ class Graph
*/
public function getKruskalMinimalSpanningTree() : Tree
{
// todo: implement
return new Tree();
}
@ -281,7 +198,6 @@ class Graph
*/
public function getPrimMinimalSpanningTree() : Tree
{
// todo: implement
return new Tree();
}
@ -405,7 +321,9 @@ class Graph
*/
public function getSize() : int
{
return \count($this->edges);
$edges = $this->getEdges();
return \count($edges);
}
/**

View File

@ -24,6 +24,14 @@ namespace phpOMS\Stdlib\Graph;
*/
class Node
{
/**
* Node id.
*
* @var string
* @since 1.0.0
*/
private string $id;
/**
* Node data.
*
@ -32,18 +40,40 @@ class Node
*/
private $data = null;
/**
* Edges.
*
* @var Edge[]
* @since 1.0.0
*/
protected array $edges = [];
/**
* Constructor.
*
* @param mixed $data Node data
* @param string $id Node id
* @param mixed $data Node data
*
* @since 1.0.0
*/
public function __construct($data = null)
public function __construct(string $id, $data = null)
{
$this->id = $id;
$this->data = $data;
}
/**
* Get node id.
*
* @return string
*
* @since 1.0.0
*/
public function getId() : string
{
return $this->id;
}
/**
* Get data.
*
@ -61,6 +91,8 @@ class Node
*
* @param mixed $data Node data
*
* @return void
*
* @since 1.0.0
*/
public function setData($data) : void
@ -73,12 +105,105 @@ class Node
*
* @param Node $node Node
*
* @return boll
* @return bool
*
* @since 1.0.0
*/
public function isEqual(Node $node) : bool
{
return true;
return $this->id === $node->getId() && $this->data === $node->getData();
}
/**
* Set a relative undirected node.
*
* @param Node $node Graph node
* @param int $key Index for absolute position
*
* @return Edge
*
* @since 1.0.0
*/
public function setNodeRelative(Node $node, int $key = null) : Edge
{
$edge = new Edge($this, $node);
$this->setEdge($edge, $key);
if (!$edge->isDirected()) {
$node->setEdge($edge);
}
return $edge;
}
/**
* Add edge to node.
*
* @param Edge $edge Graph edge
* @param int $key Index for absolute position
*
* @return Node
*
* @since 1.0.0
*/
public function setEdge(Edge $edge, int $key = null) : self
{
if ($key === null) {
$this->edges[] = $edge;
} else {
$this->edges[$key] = $edge;
}
return $this;
}
/**
* Get graph edge.
*
* @param int $key Edge key
*
* @return null|Edge
*
* @since 1.0.0
*/
public function getEdge(int $key) : ?Edge
{
return $this->edges[$key] ?? null;
}
/**
* Get graph edges
*
* @return Edge[]
*
* @since 1.0.0
*/
public function getEdges() : array
{
return $this->edges;
}
/**
* Get all node neighbors.
*
* @return Node[]
*
* @since 1.0.0
*/
public function getNeighbors() : array
{
$neighbors = [];
foreach ($this->edges as $edge) {
$nodes = $edge->getNodes();
if ($nodes[0] !== null && !$this->isEqual($nodes[0])) {
$neighbors[] = $nodes[0];
} else {
$neighbors[] = $nodes[1];
}
}
return $neighbors;
}
}