数据库 树形数据结构数据库的设计

修改预订树遍历

现在,我们来看看存储树的另一种方法。递归可能很慢,所以我们宁可不使用递归函数。我们也希望最小化数据库查询的数量。我们最好只对每个活动进行一次查询。

我们将从水平的方式展开我们的树。从根节点('Food')开始,并在其左边写1。按照树进行“水果”,并在其旁边写一个2。这样,在每个节点的左侧和右侧写一个数字时,沿着树的边缘走(遍历)。最后一个数字写在“食物”节点的右侧。在这个图像中,您可以看到整个编号的树,还有几个箭头来指示编号顺序。

1105_numbering

我们会将这些数字左右调用(例如,“食物”的左侧值为1,正确的值为18)。您可以看到,这些数字表示每个节点之间的关系。因为“红”有数字3和6,它是1-18“食物”节点的后裔。以同样的方式,我们可以说所有左值大于2,右值小于11的节点都是2-11“Fruit”的后代。树结构现在存储在左右值中。这种围绕树和计数节点走动的方法被称为“修改的预订树遍历”算法。

在我们继续之前,让我们看看这些值在我们的表中如何:

1105_table2

请注意,“left”和“right”这个词在SQL中有特殊的含义。因此,我们必须使用'lft'和'rgt'来标识列。还要注意,我们不再需要“父”列了。我们现在有lft和rgt值来存储树结构。

检索树

如果要使用具有左右值的表显示树,则首先必须标识要检索的节点。例如,如果您想要“水果”子树,则必须仅选择左侧值在2和11之间的节点。在SQL中,这将是:

SELECT * FROM tree WHERE lft BETWEEN 2 AND 11;

那回报:

1105_table3

那就是:一个查询中的一棵树。要像我们的递归函数那样显示这个树,我们必须向此查询添加一个ORDER BY子句。如果您从表中添加和删除行,则表可能不会处于正确的顺序。因此,我们应该按照左边的值排列。

SELECT * FROM tree WHERE lft BETWEEN 2 AND 11 ORDER BY lft ASC;

剩下的唯一问题就是缩进。

为了显示树状结构,儿童的缩写比父母稍微缩小一点。我们可以通过保持一堆正确的值来做到这一点。每次从一个节点的子节点开始,您将该节点的正确值添加到堆栈中。您知道该节点的所有子节点都具有小于父节点正确值的正确值,因此通过将当前节点的正确值与堆栈中的最右边节点进行比较,可以看到是否仍然展示该家长的孩子。完成显示节点后,从堆栈中删除其正确的值。如果您计算堆栈中的元素,您将获得当前节点的级别。

<?php  
function display_tree($root) {  
    // retrieve the left and right value of the $root node  
    $result = mysql_query('SELECT lft, rgt FROM tree '.  
                           'WHERE title="'.$root.'";');  
    $row = mysql_fetch_array($result);  
  
    // start with an empty $right stack  
    $right = array();  
  
    // now, retrieve all descendants of the $root node  
    $result = mysql_query('SELECT title, lft, rgt FROM tree '.  
                           'WHERE lft BETWEEN '.$row['lft'].' AND '.  
                           $row['rgt'].' ORDER BY lft ASC;');  
  
    // display each row  
    while ($row = mysql_fetch_array($result)) {  
        // only check stack if there is one  
        if (count($right)>0) {  
            // check if we should remove a node from the stack  
            while ($right[count($right)-1]<$row['rgt']) {  
                array_pop($right);  
            }  
        }  
  
        // display indented node title  
        echo str_repeat('  ',count($right)).$row['title']."n";  
  
        // add this node to the stack  
        $right[] = $row['rgt'];  
    }  
}  
?>

如果你运行这个代码,你将得到与上面讨论的递归函数完全相同的树。我们的新功能可能会更快:它不是递归的,它只使用两个查询。

节点的路径

使用这种新算法,我们还必须找到一种新方式来获取特定节点的路径。要获得这个路径,我们需要一个列表,该节点的所有祖先。

用我们新的表格结构,这真的不是很大的工作。例如,当您查看4-5个“樱桃”节点时,您会看到所有祖先的左值小于4,而所有正确的值都大于5.要获取所有祖先,我们可以使用这个查询:

SELECT title FROM tree WHERE lft < 4 AND rgt > 5 ORDER BY lft ASC;

请注意,就像我们之前的查询一样,我们必须使用ORDER BY子句对节点进行排序。此查询将返回:

+-------+
| title |
+-------+
| Food  |
| Fruit |
| Red   |
+-------+

我们现在只需要加入行以获得“樱桃”的路径。

多少后裔

如果你给我一个节点的左右值,我可以通过使用一点数学来告诉你有多少个后代。

当每个后代用2增加节点的正确值时,可以通过以下方式计算后代数:

descendants = (right – left - 1) / 2

有了这个简单的公式,我可以告诉你,2-11“果实”节点有4个后代节点,8-9个“香蕉”节点只是一个小孩,而不是一个父母。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值