<?php namespace Graphp\Algorithms\Tree; use Graphp\Algorithms\Tree\Base as Tree; use Fhaculty\Graph\Exception\UnderflowException; use Fhaculty\Graph\Exception\UnexpectedValueException; use Graphp\Algorithms\Search\Base as Search; use Graphp\Algorithms\Search\StrictDepthFirst; use Fhaculty\Graph\Vertex; use Fhaculty\Graph\Set\Vertices; use Fhaculty\Graph\Edge\Base as Edge; use Fhaculty\Graph\Edge\Undirected as UndirectedEdge; use Graphp\Algorithms\Degree; /** * Undirected tree implementation * * An undirected tree is a connected Graph (single component) with no cycles. * Every undirected Tree is an undirected Graph, but not every undirected Graph * is an undirected Tree. * * A * / \ * B C * / \ * D E * * Undirected trees do not have special root Vertices (like the above picture * might suggest). The above tree Graph can also be equivalently be pictured * like this: * * C * /|\ * / | \ * A D E * / * B * * If you're looking for a tree with a designated root Vertex, use directed, * rooted trees (BaseDirected). * * @link http://en.wikipedia.org/wiki/Tree_%28graph_theory%29 * @see BaseDirected if you're looking for directed, rooted trees */ class Undirected extends Tree { /** * checks if this is a tree * * @return boolean * @uses Vertices::isEmpty() to skip null Graphs (a Graph with no Vertices is *NOT* a valid tree) * @uses Vertices::getVertexFirst() to get get get random "root" Vertex to start search from * @uses self::getVerticesSubtreeRecursive() to count number of vertices connected to root */ public function isTree() { if ($this->graph->getVertices()->isEmpty()) { return false; } // every vertex can represent a root vertex, so just pick one $root = $this->graph->getVertices()->getVertexFirst(); $vertices = array(); try { $this->getVerticesSubtreeRecursive($root, $vertices, null); } catch (UnexpectedValueException $e) { return false; } return (count($vertices) === count($this->graph->getVertices())); } /** * checks if the given $vertex is a leaf (outermost vertex with exactly one edge) * * @param Vertex $vertex * @return boolean * @uses Degree::getDegreeVertex() */ public function isVertexLeaf(Vertex $vertex) { return ($this->degree->getDegreeVertex($vertex) === 1); } /** * checks if the given $vertex is an internal vertex (inner vertex with at least 2 edges) * * @param Vertex $vertex * @return boolean * @uses Degree::getDegreeVertex() */ public function isVertexInternal(Vertex $vertex) { return ($this->degree->getDegreeVertex($vertex) >= 2); } /** * get subtree for given Vertex and ignore path to "parent" ignoreVertex * * @param Vertex $vertex * @param Vertex[] $vertices * @param Vertex|null $ignore * @throws UnexpectedValueException for cycles or directed edges (check isTree()!) * @uses self::getVerticesNeighbor() * @uses self::getVerticesSubtreeRecursive() to recurse into sub-subtrees */ private function getVerticesSubtreeRecursive(Vertex $vertex, array &$vertices, Vertex $ignore = null) { if (isset($vertices[$vertex->getId()])) { // vertex already visited => must be a cycle throw new UnexpectedValueException('Vertex already visited'); } $vertices[$vertex->getId()] = $vertex; foreach ($this->getVerticesNeighbor($vertex) as $vertexNeighboor) { if ($vertexNeighboor === $ignore) { // ignore source vertex only once $ignore = null; continue; } $this->getVerticesSubtreeRecursive($vertexNeighboor, $vertices, $vertex); } } /** * get neighbor vertices for given start vertex * * @param Vertex $vertex * @throws UnexpectedValueException for directed edges * @return Vertices (might include possible duplicates) * @uses Vertex::getEdges() * @uses Edge::getVertexToFrom() * @see Vertex::getVerticesEdge() */ private function getVerticesNeighbor(Vertex $vertex) { $vertices = array(); foreach ($vertex->getEdges() as $edge) { /* @var Edge $edge */ if (!($edge instanceof UndirectedEdge)) { throw new UnexpectedValueException('Directed edge encountered'); } $vertices[] = $edge->getVertexToFrom($vertex); } return new Vertices($vertices); } }