当前位置: 首页 > 图文教程 > 网络编程 > PHP > php:树形结构的算法 2

PHP
php 多线程上下文中安全写文件实现代码
PHP类的使用 实例代码讲解
用php实现让页面只能被百度gogole蜘蛛访问的方法
php 学习笔记
PHP编程过程中需要了解的this,self,parent的区别
php 操作excel文件的方法小结
使用PHP获取网络文件的实现代码
PHP 巧用数组降低程序的时间复杂度
php下将XML转换为数组
php 文件上传代码(限制jpg文件)
php 无极分类(递归)实现代码
PHP 采集获取指定网址的内容
PHP 将图片按创建时间进行分类存储的实现代码
PHP 存储文本换行实现方法
PHP 批量更新网页内容实现代码
用PHP查询搜索引擎排名位置的代码
用php实现的获取网页中的图片并保存到本地的代码
php实现首页链接查询 友情链接检查的代码
处理php自动反斜杠的函数代码
php实现的遍历文件夹下所有文件,编辑删除

PHP 中的 php:树形结构的算法 2


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-08-14   浏览: 65 ::
收藏到网摘: n/a

  1 Food 18
  |
  +---------------------------------------+
  | |
  2 Fruit 11 12 Meat 17
  | |
  +------------------------+ +---------------------+
  | | | |
  3 Red 6 7 Yellow 10 13 Beef 14 15 Pork 16
  | |
  4 Cherry 5 8 Banana 9
  
  这样整个树状结构可以通过左右值来存储到数据库中。继续之前,我们看一看下面整理过的数据表。
  
  
  +-----------------------+-----+-----+
  | 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 * FROM tree WHERE lft BETWEEN 2 AND 11; 这个查询得到了以下的结果。
  
  
  +------------+-----+-----+
  | name | lft | rgt |
  +------------+-----+-----+
  | Fruit | 2 | 11 |
  | Red | 3 | 6 |
  | Cherry | 4 | 5 |
  | Yellow | 7 | 10 |
  | Banana | 8 | 9 |
  +------------+-----+-----+
  看到了吧,只要一个查询就可以得到所有这些节点。为了能够像上面的递归函数那样显示整个树状结构,我们还需要对这样的查询进行排序。用节点的左值进行排序:
  
  SELECT * FROM tree WHERE lft BETWEEN 2 AND 11 ORDER BY lft ASC;
  剩下的问题如何显示层级的缩进了。
  
  <?php
  function display_tree($root)
  {
  // 得到根节点的左右值
  $result = mysql_query('SELECT lft, rgt FROM tree '.'WHERE name="'.$root.'";');
  $row = mysql_fetch_array($result);
  
  // 准备一个空的右值堆栈
  $right = array();
  
  // 获得根基点的所有子孙节点
  $result = mysql_query('SELECT name, lft, rgt FROM tree '.
  'WHERE lft BETWEEN '.$row['lft'].' AND '.
  $row['rgt'].' ORDER BY lft ASC;');
  
  // 显示每一行
  while ($row = mysql_fetch_array($result))
  {
  // only check stack if there is one
  if (count($right)>0)
  {
  // 检查我们是否应该将节点移出堆栈
  while ($right[count($right)-1]<$row['rgt'])
  {
  array_pop($right);
  }
  }
  
  // 缩进显示节点的名称
  echo str_repeat(' ',count($right)).$row['name']."n";
  
  // 将这个节点加入到堆栈中
  $right[] = $row['rgt'];
  }
  }
  ?>
  如果你运行一下以上的函数就会得到和递归函数一样的结果。只是我们的这个新的函数可能会更快一些,因为只有2次数据库查询。 要获知一个节点的路径就更简单了,如果我们想知道Cherry 的路径就利用它的左右值4和5来做一个查询。
  
  SELECT name FROM tree WHERE lft < 4 AND rgt > 5 ORDER BY lft ASC;
  这样就会得到以下的结果:
  
  +------------+
  | name |
  +------------+
  | Food |
  | Fruit |
  | Red |
  +------------+
  那么某个节点到底有多少子孙节点呢?很简单,子孙总数=(右值-左值-1)/2 descendants = (right – left - 1) / 2 不相信?自己算一算啦。用这个简单的公式,我们可以很快的算出"Fruit 2-11"节点有4个子孙节点,而"Banana 8-9"节点没有子孙节点,也就是说它不是一个父节点了。
  很神奇吧?虽然我已经多次用过这个方法,但是每次这样做的时候还是感到很神奇。
  
  这的确是个很好的办法,但是有什么办法能够帮我们建立这样有左右值的数据表呢?这里再介绍一个函数给大家,这个函数可以将name和parent结构的表自动转换成带有左右值的数据表。
  
  
  <?php
  function rebuild_tree($parent, $left) {
  // the right value of this node is the left value + 1
  $right = $left+1;
  
  // get all children of this node
  $result = mysql_query('SELECT name FROM tree '.
  'WHERE parent="'.$parent.'";');
  while ($row = mysql_fetch_array($result)) {
  // recursive execution of this function for each
  // child of this node
  // $right is the current right value, which is
  // incremented by the rebuild_tree function
  $right = rebuild_tree($row['name'], $right);
  }
  
  // we've got the left value, and now that we've processed
  // the children of this node we also know the right value
  mysql_query('UPDATE tree SET lft='.$left.', rgt='.
  $right.' WHERE name="'.$parent.'";');
  
  // return the right value of this node + 1
  return $right+1;
  }
  ?>
  当然这个函数是一个递归函数,我们需要从根节点开始运行这个函数来重建一个带有左右值的树
  
  rebuild_tree('Food',1);
  这个函数看上去有些复杂,但是它的作用和手工对表进行编号一样,就是将立体多层结构的转换成一个带有左右值的数据表。