mirror of
https://github.com/Karaka-Management/phpOMS.git
synced 2026-01-11 17:58:41 +00:00
258 lines
5.3 KiB
PHP
258 lines
5.3 KiB
PHP
<?php
|
|
/**
|
|
* Orange Management
|
|
*
|
|
* PHP Version 7.2
|
|
*
|
|
* @package phpOMS\Stdlib\Graph
|
|
* @copyright Dennis Eichhorn
|
|
* @license OMS License 1.0
|
|
* @version 1.0.0
|
|
* @link http://website.orange-management.de
|
|
*/
|
|
declare(strict_types=1);
|
|
|
|
namespace phpOMS\Stdlib\Graph;
|
|
|
|
/**
|
|
* Tree class.
|
|
*
|
|
* @package phpOMS\Stdlib\Graph
|
|
* @license OMS License 1.0
|
|
* @link http://website.orange-management.de
|
|
* @since 1.0.0
|
|
*/
|
|
class Tree extends Graph
|
|
{
|
|
/**
|
|
* Root node.
|
|
*
|
|
* @var Node
|
|
* @since 1.0.0
|
|
*/
|
|
private $root = null;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function __construct()
|
|
{
|
|
$root = new Node();
|
|
parent::addNode($root);
|
|
}
|
|
|
|
/**
|
|
* Add a note relative to a node.
|
|
*
|
|
* @param Node $base Base node
|
|
* @param Node $node Node to add
|
|
*
|
|
* @return Tree
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function addRelativeNode(Node $base, Node $node) : Tree
|
|
{
|
|
parent::addNode($node);
|
|
parent::addEdge(new Edge($base, $node));
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get maximum tree depth.
|
|
*
|
|
* @param Node $node Tree node
|
|
*
|
|
* @return int
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function getMaxDepth(Node $node = null) : int
|
|
{
|
|
$currentNode = $node ?? $this->root;
|
|
|
|
if ($currentNode === null) {
|
|
return 0;
|
|
}
|
|
|
|
$depth = 1;
|
|
$neighbors = $this->getNeighbors($currentNode);
|
|
|
|
foreach ($neighbors as $neighbor) {
|
|
$depth = \max($depth, $depth + $this->getMaxDepth($neighbor));
|
|
}
|
|
|
|
return $depth;
|
|
}
|
|
|
|
/**
|
|
* Get minimum tree path.
|
|
*
|
|
* @param Node $node Tree node
|
|
*
|
|
* @return int
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function getMinDepth(Node $node = null) : int
|
|
{
|
|
$currentNode = $node ?? $this->root;
|
|
|
|
if ($currentNode === null) {
|
|
return 0;
|
|
}
|
|
|
|
$depth = [];
|
|
$neighbors = $this->getNeighbors($currentNode);
|
|
|
|
foreach ($neighbors as $neighbor) {
|
|
$depth[] = $this->getMaxDepth($neighbor);
|
|
}
|
|
|
|
$depth = empty($depth) ? 0 : $depth;
|
|
|
|
return \min($depth) + 1;
|
|
}
|
|
|
|
/**
|
|
* Perform task on tree nodes in level order.
|
|
*
|
|
* @param Node $node Tree node
|
|
* @param \Closure $callback Task to perform
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function levelOrder(Node $node, \Closure $callback)
|
|
{
|
|
$depth = $this->getMaxDepth();
|
|
|
|
for ($i = 1; $i < $depth; ++$i) {
|
|
$nodes = $this->getLevel($i);
|
|
callback($nodes);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if node is leaf.
|
|
*
|
|
* @param Node $node Tree node
|
|
*
|
|
* @return bool
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function isLeaf(Node $node) : bool
|
|
{
|
|
return \count($this->getEdgesOfNode($node)) === 1;
|
|
}
|
|
|
|
/**
|
|
* Get all nodes of a specific level.
|
|
*
|
|
* @param int $level Level to retrieve
|
|
* @param Node $node Tree node
|
|
*
|
|
* @return Node[]
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function getLevelNodes(int $level, Node $node) : array
|
|
{
|
|
--$level;
|
|
$neighbors = $this->getNeighbors($node);
|
|
$nodes = [];
|
|
|
|
if ($level === 1) {
|
|
return $neighbors;
|
|
}
|
|
|
|
foreach ($neighbors as $neighbor) {
|
|
array_merge($nodes, $this->getLevelNodes($level, $neighbor));
|
|
}
|
|
|
|
return $nodes;
|
|
}
|
|
|
|
/**
|
|
* Check if the tree is full.
|
|
*
|
|
* @param int $type Child nodes per non-leaf node
|
|
*
|
|
* @return bool
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function isFull(int $type) : bool
|
|
{
|
|
if (\count($this->edges) % $type !== 0) {
|
|
return false;
|
|
}
|
|
|
|
foreach ($this->nodes as $node) {
|
|
$neighbors = \count($this->getNeighbors($node));
|
|
|
|
if ($neighbors !== $type && $neighbors !== 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Perform action on tree in pre-order.
|
|
*
|
|
* @param Node $node Tree node
|
|
* @param \Closure $callback Task to perform on node
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function preOrder(Node $node, \Closure $callback) : void
|
|
{
|
|
if (\count($this->nodes) === 0) {
|
|
return;
|
|
}
|
|
|
|
$callback($node);
|
|
$neighbors = $this->getNeighbors($node);
|
|
|
|
foreach ($neighbors as $neighbor) {
|
|
// todo: get neighbors needs to return in ordered way
|
|
$this->preOrder($neighbor, $callback);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform action on tree in post-order.
|
|
*
|
|
* @param Node $node Tree node
|
|
* @param \Closure $callback Task to perform on node
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function postOrder(Node $node, \Closure $callback) : void
|
|
{
|
|
if (\count($this->nodes) === 0) {
|
|
return;
|
|
}
|
|
|
|
$neighbors = $this->getNeighbors($node);
|
|
|
|
foreach ($neighbors as $neighbor) {
|
|
// todo: get neighbors needs to return in ordered way
|
|
$this->postOrder($neighbor, $callback);
|
|
}
|
|
|
|
$callback($node);
|
|
}
|
|
}
|