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执行速度全攻略(下)
Oct 09 PHP
PHP的面试题集,附我的答案和分析(一)
Nov 19 PHP
PHP获取类中常量,属性,及方法列表的方法
Apr 09 PHP
使用php伪造referer的方法 利用referer防止图片盗链
Jan 20 PHP
php中json_encode UTF-8中文乱码的更好解决方法
Sep 28 PHP
检测codeigniter脚本消耗内存情况的方法
Mar 21 PHP
php简单生成一组与多组随机字符串的方法
May 09 PHP
PHP使用mongoclient简单操作mongodb数据库示例
Feb 08 PHP
laravel实现简单用户权限的示例代码
May 28 PHP
解决Laravel blade模板转义html标签的问题
Sep 03 PHP
laravel执行php artisan migrate报错的解决方法
Oct 09 PHP
PHP7 windows支持
Mar 09 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
Linux下进行MYSQL编程时插入中文乱码的解决方案
2007/03/15 PHP
基于PHP遍历数组的方法汇总分析
2013/06/08 PHP
JS(jQuery)实现聊天接收到消息语言自动提醒功能详解【提示“您有新的消息请注意查收”】
2019/04/16 PHP
JQuery Dialog(JS 模态窗口,可拖拽的DIV)
2010/02/07 Javascript
js 页面关闭前的出现提示的实现代码
2011/05/25 Javascript
jquery操作select option 的代码小结
2011/06/21 Javascript
javascript日期转换 时间戳转日期格式
2011/11/05 Javascript
Javascript非构造函数的继承
2015/04/27 Javascript
jQuery简单实现仿京东商城的左侧菜单效果代码
2015/09/09 Javascript
jQuery Easyui学习教程之实现datagrid在没有数据时显示相关提示内容
2016/07/09 Javascript
用jQuery向div中添加Html文本内容的简单实现
2016/07/13 Javascript
Node.js开启Https的实践详解
2016/10/25 Javascript
详解jQuery的表单验证插件--Validation
2016/12/21 Javascript
js a标签点击事件
2017/03/30 Javascript
JS实现禁止用户使用Ctrl+鼠标滚轮缩放网页的方法
2017/04/28 Javascript
Angular中的$watch方法详解
2017/09/18 Javascript
JS html事件冒泡和事件捕获操作示例
2019/05/01 Javascript
ES11屡试不爽的新特性,你用上了几个
2020/10/21 Javascript
python 匹配url中是否存在IP地址的方法
2018/06/04 Python
设置python3为默认python的方法
2018/10/31 Python
对Python3之进程池与回调函数的实例详解
2019/01/22 Python
python 中值滤波,椒盐去噪,图片增强实例
2019/12/18 Python
Python实时监控网站浏览记录实现过程详解
2020/07/14 Python
简单总结CSS3中视窗单位Viewport的常见用法
2016/02/04 HTML / CSS
Emporio Armani腕表天猫官方旗舰店:乔治·阿玛尼为年轻人设计的副线品牌
2017/07/02 全球购物
师范生自荐信范文
2013/10/06 职场文书
银行职业规划书范文
2013/12/28 职场文书
校园奶茶店创业计划书
2014/01/23 职场文书
市场部管理制度
2014/02/02 职场文书
孝老爱亲模范事迹材料
2014/05/25 职场文书
文员转正自我鉴定怎么写
2014/09/29 职场文书
2014年评职称工作总结
2014/11/20 职场文书
世界气象日活动总结
2015/02/27 职场文书
离婚撤诉申请书范本
2015/05/18 职场文书
vue Element-ui表格实现树形结构表格
2021/06/07 Vue.js
python使用PySimpleGUI设置进度条及控件使用
2021/06/10 Python