您的位置:首页 > 编程语言 > PHP开发

php:树形结构的算法2

2008-05-01 04:57 633 查看
.Ucg461{display:none;} 1Food18
|
+---------------------------------------+
||
2Fruit1112Meat17
||
+------------------------++---------------------+
||||
3Red67Yellow1013Beef1415Pork16
||
4Cherry58Banana9

这样整个树状结构可以通过左右值来存储到数据库中。继续之前,我们看一看下面整理过的数据表。

+-----------------------+-----+-----+
|parent|name|lft|rgt|
+-----------------------+-----+-----+
||Food|1|18|
|Food|Fruit|2|11|
|Fruit|Red|3|6|
|Red|Cherry|4|5|
|Fruit|Yellow|7|10|
|Yellow|Banana|8|9|
|Food|Meat|12|17|
|Meat|Beef|13|14|
|Meat|Pork|15|16|
+-----------------------+-----+-----+
注意:由于"left"和"right"在SQL中有特殊的意义,所以我们需要用"lft"和"rgt"来表示左右字段。另外这种结构中不再需要"parent"字段来表示树状结构。也就是说下面这样的表结构就足够了。

+------------+-----+-----+
|name|lft|rgt|
+------------+-----+-----+
|Food|1|18|
|Fruit|2|11|
|Red|3|6|
|Cherry|4|5|
|Yellow|7|10|
|Banana|8|9|
|Meat|12|17|
|Beef|13|14|
|Pork|15|16|
+------------+-----+-----+
好了我们现在可以从数据库中获取数据了,例如我们需要得到"Fruit"项下的所有所有节点就可以这样写查询语句:SELECT*FROMtreeWHERElftBETWEEN2AND11;这个查询得到了以下的结果。

+------------+-----+-----+
|name|lft|rgt|
+------------+-----+-----+
|Fruit|2|11|
|Red|3|6|
|Cherry|4|5|
|Yellow|7|10|
|Banana|8|9|
+------------+-----+-----+
看到了吧,只要一个查询就可以得到所有这些节点。为了能够像上面的递归函数那样显示整个树状结构,我们还需要对这样的查询进行排序。用节点的左值进行排序:

SELECT*FROMtreeWHERElftBETWEEN2AND11ORDERBYlftASC;
剩下的问题如何显示层级的缩进了。

<?php
functiondisplay_tree($root)
{
//得到根节点的左右值
$result=mysql_query('SELECTlft,rgtFROMtree'.'WHEREname="'.$root.'";');
$row=mysql_fetch_array($result);

//准备一个空的右值堆栈
$right=array();

//获得根基点的所有子孙节点
$result=mysql_query('SELECTname,lft,rgtFROMtree'.
'WHERElftBETWEEN'.$row['lft'].'AND'.
$row['rgt'].'ORDERBYlftASC;');

//显示每一行
while($row=mysql_fetch_array($result))
{
//onlycheckstackifthereisone
if(count($right)>0)
{
//检查我们是否应该将节点移出堆栈
while($right[count($right)-1]<$row['rgt'])
{
array_pop($right);
}
}

//缩进显示节点的名称
echostr_repeat('',count($right)).$row['name']."n";

//将这个节点加入到堆栈中
$right[]=$row['rgt'];
}
}
?>
如果你运行一下以上的函数就会得到和递归函数一样的结果。只是我们的这个新的函数可能会更快一些,因为只有2次数据库查询。要获知一个节点的路径就更简单了,如果我们想知道Cherry的路径就利用它的左右值4和5来做一个查询。

SELECTnameFROMtreeWHERElft<4ANDrgt>5ORDERBYlftASC;
这样就会得到以下的结果:

+------------+
|name|
+------------+
|Food|
|Fruit|
|Red|
+------------+
那么某个节点到底有多少子孙节点呢?很简单,子孙总数=(右值-左值-1)/2descendants=(right–left-1)/2不相信?自己算一算啦。用这个简单的公式,我们可以很快的算出"Fruit2-11"节点有4个子孙节点,而"Banana8-9"节点没有子孙节点,也就是说它不是一个父节点了。
很神奇吧?虽然我已经多次用过这个方法,但是每次这样做的时候还是感到很神奇。

这的确是个很好的办法,但是有什么办法能够帮我们建立这样有左右值的数据表呢?这里再介绍一个函数给大家,这个函数可以将name和parent结构的表自动转换成带有左右值的数据表。

<?php
functionrebuild_tree($parent,$left){
//therightvalueofthisnodeistheleftvalue+1
$right=$left+1;

//getallchildrenofthisnode
$result=mysql_query('SELECTnameFROMtree'.
'WHEREparent="'.$parent.'";');
while($row=mysql_fetch_array($result)){
//recursiveexecutionofthisfunctionforeach
//childofthisnode
//$rightisthecurrentrightvalue,whichis
//incrementedbytherebuild_treefunction
$right=rebuild_tree($row['name'],$right);
}

//we'vegottheleftvalue,andnowthatwe'veprocessed
//thechildrenofthisnodewealsoknowtherightvalue
mysql_query('UPDATEtreeSETlft='.$left.',rgt='.
$right.'WHEREname="'.$parent.'";');

//returntherightvalueofthisnode+1
return$right+1;
}
?>
当然这个函数是一个递归函数,我们需要从根节点开始运行这个函数来重建一个带有左右值的树

rebuild_tree('Food',1);
这个函数看上去有些复杂,但是它的作用和手工对表进行编号一样,就是将立体多层结构的转换成一个带有左右值的数据表。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: