PHP实现无限级分类(不使用递归)


Posted in PHP onOctober 22, 2015

无限级分类在开发中经常使用,例如:部门结构、文章分类。无限级分类的难点在于“输出”和“查询”,例如

  • 将文章分类输出为<ul>列表形式;
  • 查找分类A下面所有分类包含的文章。

1.实现原理
几种常见的实现方法,各有利弊。其中“改进前序遍历树”数据结构,便于输出和查询,但是在移动分类和常规理解上有些复杂。

2.数据结构

PHP实现无限级分类(不使用递归)

<?php
 $list = array(
 array('id'=>1, 'fid'=>0, 'title' => '中国'), 
 array('id'=>2, 'fid'=>1, 'title' => '江苏'),
 array('id'=>3, 'fid'=>1, 'title' => '安徽'),
 array('id'=>4, 'fid'=>8, 'title' => '江阴'),
 array('id'=>5, 'fid'=>3, 'title' => '芜湖'),
 array('id'=>6, 'fid'=>3, 'title' => '合肥'),
 array('id'=>7, 'fid'=>3, 'title' => '蚌埠'),
 array('id'=>8, 'fid'=>8, 'title' => '无锡')
 );
?>

各分类之间通过父类id(即fid)进行级别“串联”,形成一棵分类树。在进行串联时候有一点值得注意:分类A的fid不可以是其子类的id。

在使用这种数据结构进行输出时最常用的算法就是“递归”,熟悉PHP语言的朋友肯定知道,PHP不擅长递归 ,而且递归次数有限(100次左右,因操作系统和配置而异)。

由于所有的递归均可以使用循环实现,本文根据PHP语言特点编写了一套关于“无限级”分类的函数,相比递归实现而言效率更高。

3.输出ul列表形式
将上述数据输出为下面的HTML

<ul>
 <li class="first-child">
 <div>江苏</div>
 <ul>
 <li class="first-child last-child">
 <div>无锡</div>
 <ul>
 <li class="first-child last-child">
 <div>江阴</div>
 </li>
 </ul>
 </li>
 </ul>
 </li>
 <li class="last-child">
 <div>安徽</div>
 <ul>
 <li class="first-child"><div>芜湖</div></li>
 <li><div>合肥</div></li>
 <li class="last-child"><div>蚌埠</div></li>
 </ul>
 </li>
</ul>

这种HTML结构在前端使用(使用JavaScript和CSS构造可折叠树)十分方便。具体实现程序如下:

<ul><?php echo get_tree_ul($list, 1); ?></ul>

4.输出option列表形式

<select>
 <option value="2">江苏</option>
 <option value="8">    无锡</option>
 <option value="4">        江阴</option>
 <option value="3">安徽</option>
 <option value="5">    芜湖</option>
 <option value="6">    合肥</option>
 <option value="7">    蚌埠</option>
</select>

具体实现程序如下:

<select>
<?php
 // get_tree_option()返回数组,并为每个元素增加了“深度”(即depth)列,直接输出即可
 $options = get_tree_option($list, 1); 
 foreach($options as $op) {
 echo '<option value="' . $op['id'] .'">' . str_repeat(" ", $op['depth'] * 4) . $op['title'] . '<;/option>';
 }
?>
<;/select>

5. 查找某一分类的所有子类

<?php
 $children = get_tree_child($list, 0);
 echo implode(',', $children); // 输出:1,3,2,7,6,5,8,4
?>

6. 查找某一分类的所有父类

<?php
 $children = get_tree_parent($list, 4);
 echo implode(',', $children); //8, 2, 10
?>

7. 相关函数

<?php
function get_tree_child($data, $fid) {
 $result = array();
 $fids = array($fid);
 do {
 $cids = array();
 $flag = false;
 foreach($fids as $fid) {
 for($i = count($data) - 1; $i >=0 ; $i--) {
 $node = $data[$i];
 if($node['fid'] == $fid) {
 array_splice($data, $i , 1);
 $result[] = $node['id'];
 $cids[] = $node['id'];
 $flag = true;
 }
 }
 }
 $fids = $cids;
 } while($flag === true);
 return $result;
}

function get_tree_parent($data, $id) {
 $result = array();
 $obj = array();
 foreach($data as $node) {
 $obj[$node['id']] = $node;
 } 

 $value = isset($obj[$id]) ? $obj[$id] : null; 
 while($value) {
 $id = null;
 foreach($data as $node) {
 if($node['id'] == $value['fid']) {
 $id = $node['id'];
 $result[] = $node['id'];
 break;
 }
 }
 if($id === null) {
 $result[] = $value['fid'];
 }
 $value = isset($obj[$id]) ? $obj[$id] : null;
 }
 unset($obj);
 return $result;
}

function get_tree_ul($data, $fid) {
 $stack = array($fid);
 $child = array();
 $added_left = array();
 $added_right= array();
 $html_left = array();
 $html_right = array();
 $obj = array();
 $loop = 0;
 foreach($data as $node) {
 $pid = $node['fid'];
 if(!isset($child[$pid])) {
 $child[$pid] = array();
 }
 array_push($child[$pid], $node['id']);
 $obj[$node['id']] = $node;
 }

 while (count($stack) > 0) { 
 $id = $stack[0];
 $flag = false;
 $node = isset($obj[$id]) ? $obj[$id] : null;
 if (isset($child[$id])) {
 $cids = $child[$id];
 $length = count($cids);
 for($i = $length - 1; $i >= 0; $i--) {
 array_unshift($stack, $cids[$i]);
 }
 $obj[$cids[$length - 1]]['isLastChild'] = true;
 $obj[$cids[0]]['isFirstChild'] = true;
 $flag = true;
 }
 if ($id != $fid && $node && !isset($added_left[$id])) {
 if(isset($node['isFirstChild']) && isset($node['isLastChild'])) {
 $html_left[] = '<li class="first-child last-child">';
 } else if(isset($node['isFirstChild'])) {
 $html_left[] = '<li class="first-child">';
 } else if(isset($node['isLastChild'])) {
 $html_left[] = '<li class="last-child">';
 } else {
 $html_left[] = '<li>';
 } 
 $html_left[] = ($flag === true) ? "<div>{$node['title']}</div><ul>" : "<div>{$node['title']}</div>";
 $added_left[$id] = true;
 } 
 if ($id != $fid && $node && !isset($added_right[$id])) {
 $html_right[] = ($flag === true) ? '</ul></li>' : '</li>';
 $added_right[$id] = true;
 }

 if ($flag == false) {
 if($node) {
 $cids = $child[$node['fid']];
 for ($i = count($cids) - 1; $i >= 0; $i--) {
 if ($cids[$i] == $id) {
 array_splice($child[$node['fid']], $i, 1);
 break;
 }
 } 
 if(count($child[$node['fid']]) == 0) {
 $child[$node['fid']] = null;
 }
 }
 array_push($html_left, array_pop($html_right));
 array_shift($stack);
 }
 $loop++;
 if($loop > 5000) return $html_left;
 }
 unset($child);
 unset($obj);
 return implode('', $html_left);
}

function get_tree_option($data, $fid) {
 $stack = array($fid);
 $child = array();
 $added = array();
 $options = array();
 $obj = array();
 $loop = 0;
 $depth = -1;
 foreach($data as $node) {
 $pid = $node['fid'];
 if(!isset($child[$pid])) {
 $child[$pid] = array();
 }
 array_push($child[$pid], $node['id']);
 $obj[$node['id']] = $node;
 }

 while (count($stack) > 0) { 
 $id = $stack[0];
 $flag = false;
 $node = isset($obj[$id]) ? $obj[$id] : null;
 if (isset($child[$id])) {
 for($i = count($child[$id]) - 1; $i >= 0; $i--) {
 array_unshift($stack, $child[$id][$i]);
 }
 $flag = true;
 }
 if ($id != $fid && $node && !isset($added[$id])) {
 $node['depth'] = $depth;
 $options[] = $node;
 $added[$id] = true;
 }
 if($flag == true){
 $depth++;
 } else {
 if($node) {
 for ($i = count($child[$node['fid']]) - 1; $i >= 0; $i--) {
 if ($child[$node['fid']][$i] == $id) {
 array_splice($child[$node['fid']], $i, 1);
 break;
 }
 } 
 if(count($child[$node['fid']]) == 0) {
 $child[$node['fid']] = null;
 $depth--;
 }
 }
 array_shift($stack);
 }
 $loop++;
 if($loop > 5000) return $options;
 }
 unset($child);
 unset($obj);
 return $options;
}
?>

以上介绍的就是在不使用递归的情况下php实现无限极分类,希望对大家的学习有所帮助。

PHP 相关文章推荐
php xml文件操作实现代码(二)
Mar 20 PHP
php5.5中类级别的常量使用介绍
Oct 02 PHP
php轻松实现中英文混排字符串截取
May 28 PHP
PHP SPL标准库之文件操作(SplFileInfo和SplFileObject)实例
May 11 PHP
php使用GD库创建图片缩略图的方法
Jun 10 PHP
php过滤所有的空白字符(空格、全角空格、换行等)
Oct 27 PHP
使用Huagepage和PGO来提升PHP7的执行性能
Nov 30 PHP
浅谈PHP中静态方法和非静态方法的相互调用
Oct 04 PHP
PHP的mysqli_stat()函数讲解
Jan 23 PHP
PHP 代码简洁之道(小结)
Oct 16 PHP
基于Laravel 多个中间件的执行顺序详解
Oct 21 PHP
laravel5.6 框架邮件队列database驱动简单demo示例
Jan 26 PHP
PHP实现递归无限级分类
Oct 22 #PHP
php防止网站被攻击的应急代码
Oct 21 #PHP
PHP统计当前在线用户数实例讲解
Oct 21 #PHP
php精确的统计在线人数的方法
Oct 21 #PHP
为你总结一些php信息函数
Oct 21 #PHP
深入理解PHP变量的值类型和引用类型
Oct 21 #PHP
为你总结一些php系统类函数
Oct 21 #PHP
You might like
15种PHP Encoder的比较
2007/04/17 PHP
php中++i 与 i++ 的区别
2012/08/08 PHP
Laravel框架实现利用中间件进行操作日志记录功能
2018/06/06 PHP
PHP 结合 Boostrap 结合 js 实现学生列表删除编辑及搜索功能
2019/05/21 PHP
javascript中解析四则运算表达式的算法和示例
2014/08/11 Javascript
AngularJS学习笔记之ng-options指令
2015/06/16 Javascript
JS实现从顶部下拉显示的带动画QQ客服特效代码
2015/10/24 Javascript
学习JavaScript鼠标响应事件
2015/12/25 Javascript
Node.js环境下编写爬虫爬取维基百科内容的实例分享
2016/06/12 Javascript
Node.js测试中的Mock文件系统详解
2016/11/21 Javascript
jQuery插件FusionCharts绘制的3D饼状图效果实例【附demo源码下载】
2017/03/03 Javascript
微信小程序wepy框架学习和使用心得详解
2019/05/24 Javascript
使用JavaScript获取Django模板指定键值数据
2020/05/27 Javascript
整理Python 常用string函数(收藏)
2016/05/30 Python
Python实现聊天机器人的示例代码
2018/07/09 Python
详解安装mitmproxy以及遇到的坑和简单用法
2019/01/21 Python
python Matplotlib底图中鼠标滑过显示隐藏内容的实例代码
2019/07/31 Python
解决Python设置函数调用超时,进程卡住的问题
2019/08/08 Python
pytorch 模拟关系拟合——回归实例
2020/01/14 Python
django ListView的使用 ListView中获取url中的参数值方式
2020/03/27 Python
Python中的wordcloud库安装问题及解决方法
2020/05/27 Python
对python中list的五种查找方法说明
2020/07/13 Python
记录一下scrapy中settings的一些配置小结
2020/09/28 Python
Spongelle官网:美国的创意护肤洗护品牌
2019/05/15 全球购物
工程现场管理求职自荐信
2013/10/02 职场文书
外贸业务员岗位职责
2013/11/24 职场文书
继承公证书
2014/04/09 职场文书
《数星星的孩子》教学反思
2014/04/11 职场文书
学校地质灾害防治方案
2014/06/10 职场文书
无房产证房屋转让协议书合同样本
2014/10/18 职场文书
群众路线教育实践活动实施方案
2014/10/31 职场文书
房屋租赁意向书范本
2015/05/09 职场文书
详解vue中v-for的key唯一性
2021/05/15 Vue.js
python中的None与NULL用法说明
2021/05/25 Python
Redis keys命令的具体使用
2022/06/05 Redis
Go结合Gin导出Mysql数据到Excel表格
2022/08/05 Golang