编辑:好的,我对此进行了更多研究。我认为命名法有混淆。您不是在寻找data structure for a transveral tree
PHP 中的一个。你想使用一棵树作为 PHP 中的数据结构,并且你想使用一种名为modified preorder tree traversal algorithm
.
报价:
When working with a tree, we work from left to right, one layer at a time, descending to each node's children before assigning a right-hand number and moving on to the right. This approach is called the modified preorder tree traversal algorithm.
这是关于在 PHP 与 MySQL 中存储分层数据。在 PHP 中,我们可以使用一个简单的树。问题是在 MySQL 的平面数据库中存储一棵树并不容易。一种选择是获取 PHP 并从中检索和邻接列表。这本质上是每个项目及其父项的列表。这种做事方式有一些缺点。
另一种方法是从 PHP 树中提取信息,该信息描述了可以由分层数据构成的嵌套集。要从 PHP 树中获取此信息,我们需要使用修改后的前序树遍历算法。这是一种上下运行树以便从中提取某些信息的方法。
无论我们使用邻接列表模型还是修改后的前序树遍历来检索信息,我们都使用完全相同的 PHP Tree。区别在于我们如何从树中检索信息以及如何将信息存储在 MySQL 中。如何从 MySQL 中提取信息的代码已经在您引用的页面上。要在 PHP 和 MySQL 之间同步数据,您只需使用该页面上描述的 MySQL 技术和 PHP 树类。
为此,我在 PHP 中创建了一个存储树的类。它使用一个节点。每个节点都可以被认为是完整树的根,因为可以从每个节点访问完整的子树。将节点从树中分离出来更容易,而且开销也更少。
该类的重要部分是showAdjacency方法。这将使用修改后的前序树遍历运行树,并显示每个名称的lft和rgt数量,使您能够将数据作为嵌套集存储在 MySQL 中。
您还可以显示树,以便将其可视化。此类中缺少删除方法。当您实现它时,您必须将已删除节点的子节点传递给节点的父节点。也许我稍后会这样做。
我将在帖子底部包含整个类,但以下是为修改的预排序树遍历检索数据的方式:
// The modified preorder tree traversal.
private function showAdjacency($root, $spaces)
{
// Print the data first
if ($root)
{
// On the way down the tree, we get lft.
$left = ++$spaces;
foreach( $root->next as $child)
{
if ($child)
{
$spaces = $this->showAdjacency($child, $spaces);
}
}
}
// On the way back up, we get rgt.
$right = ++$spaces;
echo "$left - $right - $root->data <br/>";
return $spaces;
}
您显然可以将 $root->data、$rgt 和 $lft 存储在用于与数据库同步的数组中。
这是整个班级。课程结束后,我使用您链接到的页面中的示例数据创建了一个树,并输出了 lft 和 rgt 值以及树的可视化。
您可以在键盘上运行代码
<?php
// Class defintions and methods:
// It's easier to use separate nodes. Each node still points to an entire and complete subtree.
class Node
{
public $data;
public $next = array();
}
// A first try at creating a tree with any number of branches from its nodes
// by Peter Ajtai - feel free to use and modify
class Tree
{
// The root
private $root;
public function __construct()
{
$this->root = NULL;
}
// The public wrapper.
// This is needed because we must start the recursive functions
// at the root, and we do not want to give public access to root.
// I am not familiar w overloading in PHP, maybe __set should be used for this
public function insertPub($data, $parent)
{
$root =& $this->root;
$this->insert($root, $data, $parent);
}
private function insert(&$root, $data, $parent)
{
// Create the root of the entire tree if no parent is passed in
if (!$root && !$parent)
{
$root = new Node;
$temp =& $root;
$temp->data = $data;
// Create space for next insertion
$temp->next[] = NULL;
} else if ($root->data == $parent)
{
// Add data as a child if we're at the parent, and we're done.
// Find the empty node to insert at
foreach($root->next as &$child)
{
// Insert at the first (and only) NULL
if (!$child)
{
$child = new Node;
$temp =& $child;
$temp->data = $data;
// Create space for next insertion
$temp->next[] = NULL;
}
}
// Create space for the next insertion
$root->next[] = NULL;
} else
{
// Keep searching for the parent as default behavior.
foreach($root->next as $child)
{
if ($child)
{
$this->insert($child, $data, $parent);
}
}
}
}
// Public wrapper for the display function.
public function showAdjPub()
{
echo "The neighbors:<br/><br/>";
$root =& $this->root;
$this->showAdjacency($root, 0);
echo "<br/>";
}
// The display function.
private function showAdjacency($root, $spaces)
{
// Print the data first
if ($root)
{
$left = ++$spaces;
foreach( $root->next as $child)
{
if ($child)
{
$spaces = $this->showAdjacency($child, $spaces);
}
}
}
$right = ++$spaces;
echo "$left - $right - $root->data <br/>";
return $spaces;
}
// Public wrapper for the display function.
public function showAllPub()
{
echo "The tree:<br/><br/>";
$root =& $this->root;
$this->showAll($root, 0);
echo "<br/>";
}
// The display function.
private function showAll($root, $spaces)
{
// Print the data first
if ($root)
{
for ($i=0; $i < $spaces; ++$i)
echo "---> ";
echo "$root->data <br/>";
++$spaces;
foreach( $root->next as $child)
{
if ($child)
{
$this->showAll($child, $spaces);
}
}
}
}
}
// Create a new instance of the tree
$myTree = new Tree;
// Insert the data into the tree.
// The following data would be retrieved using MySQL
$name = 'Electronics'; $parent = NULL;
$myTree->insertPub($name, $parent);
$name = 'Televisions'; $parent = 'Electronics';
$myTree->insertPub($name, $parent);
$name = 'Tube'; $parent = 'Televisions';
$myTree->insertPub($name, $parent);
$name = 'Lcd'; $parent = 'Televisions';
$myTree->insertPub($name, $parent);
$name = 'Plasma'; $parent = 'Televisions';
$myTree->insertPub($name, $parent);
$name = 'Portable Electronics'; $parent = 'Electronics';
$myTree->insertPub($name, $parent);
$name = 'MP3 Players'; $parent = 'Portable Electronics';
$myTree->insertPub($name, $parent);
$name = 'Flash'; $parent = 'MP3 Players';
$myTree->insertPub($name, $parent);
$name = 'CD Players'; $parent = 'Portable Electronics';
$myTree->insertPub($name, $parent);
$name = '2 Way Radios'; $parent = 'Portable Electronics';
$myTree->insertPub($name, $parent);
// Display adjacency
$myTree->showAdjPub();
// Show hierarchy
$myTree->showAllPub();
?>
PS:像您建议的那样将PHP中的数据存储在嵌套数组中会非常困难。在上面的类中,如果删除数据成员,则修改树(包括添加整个子树等),仍然可以正确检索lft
和值。rgt
如果您使用数组来存储信息,您将很难删除既有父母又有孩子的项目,并且更新 lft 和 rgt 值将非常困难。最后将大集合(子树)添加到数组中也非常困难。
树确实是存储这种分层数据的理想方式。它模仿了我们的集合概念。问题是,虽然 PHP 很容易存储树,但 MySQL 却没有,所以我们需要完成修改后的前序树遍历的所有困难工作,以便从 PHP 树中提取信息,以便我们可以将其存储在 MySQL 数据库中。