php:树形结构的算法2

<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
 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);
  这个函数看上去有些复杂,但是它的作用和手工对表进行编号一样,就是将立体多层结构的转换成一个带有左右值的数据表。

<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值