mirror of
https://github.com/Karaka-Management/phpOMS.git
synced 2026-01-11 09:48:40 +00:00
started graph implementation
This commit is contained in:
parent
5d43137f3c
commit
8c0905c39b
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user