PHP实现无限极分类的两种方式示例【递归和引用方式】


Posted in PHP onMarch 25, 2019

本文实例讲述了PHP实现无限极分类的两种方式。分享给大家供大家参考,具体如下:
面试的时候被问到无限极分类的设计和实现,比较常见的做法是在建表的时候,增加一个PID字段用来区别自己所属的分类

$array = array(
array('id' => 1, 'pid' => 0, 'name' => '河北省'),
array('id' => 2, 'pid' => 0, 'name' => '北京市'),
array('id' => 3, 'pid' => 1, 'name' => '邯郸市'),
array('id' => 4, 'pid' => 2, 'name' => '朝阳区'),
array('id' => 5, 'pid' => 2, 'name' => '通州区'),
array('id' => 6, 'pid' => 4, 'name' => '望京'),
array('id' => 7, 'pid' => 4, 'name' => '酒仙桥'),
array('id' => 8, 'pid' => 3, 'name' => '永年区'),
array('id' => 9, 'pid' => 1, 'name' => '武安市'),
);

数据在数据库中存储大概是这个样子,怎么实现无限极递归呢,有两种常用的做法,递归和引用算法

递归算法

/**
* 递归实现无限极分类
* @param $array 分类数据
* @param $pid 父ID
* @param $level 分类级别
* @return $list 分好类的数组 直接遍历即可 $level可以用来遍历缩进
*/
function getTree($array, $pid =0, $level = 0){
    //声明静态数组,避免递归调用时,多次声明导致数组覆盖
    static $list = [];
    foreach ($array as $key => $value){
      //第一次遍历,找到父节点为根节点的节点 也就是pid=0的节点
      if ($value['pid'] == $pid){
        //父节点为根节点的节点,级别为0,也就是第一级
        $value['level'] = $level;
        //把数组放到list中
        $list[] = $value;
        //把这个节点从数组中移除,减少后续递归消耗
        unset($array[$key]);
        //开始递归,查找父ID为该节点ID的节点,级别则为原级别+1
        getTree($array, $value['id'], $level+1);
      }
    }
    return $list;
}
/*
* 获得递归完的数据,遍历生成分类
*/
$array = getTree($array);
foreach($array) as $value{
    echo str_repeat('--', $value['level']), $value['name'].'<br />';
}

输出结果 无限极分类实现ok

河北省
--邯郸市
----永年区
--武安市
北京市
--朝阳区
----望京
----酒仙桥
--通州区

引用算法

function generateTree($array){
  //第一步 构造数据
  $items = array();
  foreach($array as $value){
    $items[$value['id']] = $value;
  }
  //第二部 遍历数据 生成树状结构
  $tree = array();
  foreach($items as $key => $value){
    if(isset($items[$item['pid']])){
      $items[$item['pid']]['son'][] = &$items[$key];
    }else{
      $tree[] = &$items[$key];
    }
  }
  return $tree;
}
//经过第一步 数据变成了这样
Array
(
  [1] => Array
    (
      [id] => 1
      [pid] => 0
      [name] => 河北省
      [children] => Array
        (
        )
    )
  [2] => Array
    (
      [id] => 2
      [pid] => 0
      [name] => 北京市
      [children] => Array
        (
        )
    )
  [3] => Array
    (
      [id] => 3
      [pid] => 1
      [name] => 邯郸市
      [children] => Array
        (
        )
    )
  [4] => Array
    (
      [id] => 4
      [pid] => 2
      [name] => 朝阳区
      [children] => Array
        (
        )
    )
  [5] => Array
    (
      [id] => 5
      [pid] => 2
      [name] => 通州区
      [children] => Array
        (
        )
    )
  [6] => Array
    (
      [id] => 6
      [pid] => 4
      [name] => 望京
      [children] => Array
        (
        )
    )
  [7] => Array
    (
      [id] => 7
      [pid] => 4
      [name] => 酒仙桥
      [children] => Array
        (
        )
    )
  [8] => Array
    (
      [id] => 8
      [pid] => 3
      [name] => 永年区
      [children] => Array
        (
        )
    )
  [9] => Array
    (
      [id] => 9
      [pid] => 1
      [name] => 武安市
      [children] => Array
        (
        )
    )
)
//第一步很容易就能看懂,就是构造数据,现在咱们仔细说一下第二步
 $tree = array();
 //遍历构造的数据
  foreach($items as $key => $value){
  //如果pid这个节点存在
    if(isset($items[$value['pid']])){
      //把当前的$value放到pid节点的son中 注意 这里传递的是引用 为什么呢?
      $items[$value['pid']]['son'][] = &$items[$key];
    }else{
      $tree[] = &$items[$key];
    }
  }
//这个方法的核心在于引用,php变量默认的传值方式是按指传递
//也就是说 假如说 遍历顺序是 河北省 邯郸市 当遍历到河北省时 会把河北省放到tree中 遍历到邯郸市时 会把邯郸市放到河北省的子节点数组中 但是!!! 这会儿的tree数组中 河北省已经放进去了 根据php变量按值传递的规则 你并没有更改tree数组中的河北省的数据 所以这里用到了引用传递
//当你对河北省做更改时,tree数组中的河北省也一并做了更改 下面我们做个实验 我们把引用传递去掉,看一下结果
//使用普通传值输出结果
 Array
(
  [0] => Array
    (
      [id] => 1
      [pid] => 0
      [name] => 河北省
    )
  [1] => Array
    (
      [id] => 2
      [pid] => 0
      [name] => 北京市
    )
)
//可以看到 只有河北省和北京市输出出来了 因为他们俩是第一级节点 而且排行1和2,放到$tree数组中之后,没有使用引用传递,那么后续对他俩的子节点的操作都没有在$tree中生效,现在我们更改一下顺序 把邯郸市放到河北省的前面 那么根据咱们的推断 那么邯郸市就应该出现在tree数组里
//邯郸市放到河北省前面的输出结果
Array
(
  [0] => Array
    (
      [id] => 1
      [pid] => 0
      [name] => 河北省
      [son] => Array
        (
          [0] => Array
            (
              [id] => 3
              [pid] => 1
              [name] => 邯郸市
            )
        )
    )
  [1] => Array
    (
      [id] => 2
      [pid] => 0
      [name] => 北京市
    )
)
//果然是这样 那么证明我们的推断是正确的 现在我们把引用传值改回去 再看一下
//使用引用传值输出结果
Array
(
  [1] => Array
    (
      [id] => 1
      [pid] => 0
      [name] => 河北省
      [children] => Array
        (
          [0] => Array
            (
              [id] => 3
              [pid] => 1
              [name] => 邯郸市
              [children] => Array
                (
                  [0] => Array
                    (
                      [id] => 8
                      [pid] => 3
                      [name] => 永年区
                    )
                )
            )
          [1] => Array
            (
              [id] => 9
              [pid] => 1
              [name] => 武安市
            )
        )
    )
  [2] => Array
    (
      [id] => 2
      [pid] => 0
      [name] => 北京市
      [children] => Array
        (
          [0] => Array
            (
              [id] => 4
              [pid] => 2
              [name] => 朝阳区
              [children] => Array
                (
                  [0] => Array
                    (
                      [id] => 6
                      [pid] => 4
                      [name] => 望京
                    )
                  [1] => Array
                    (
                      [id] => 7
                      [pid] => 4
                      [name] => 酒仙桥
                    )
                )
            )
          [1] => Array
            (
              [id] => 5
              [pid] => 2
              [name] => 通州区
            )
        )
    )
)
//树状结构完美的输出出来了 这个方法的核心就是引用传值

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
多重?l件?合查?(二)
Oct 09 PHP
php文档更新介绍
Jul 22 PHP
php学习之function的用法
Jul 14 PHP
phpmyadmin config.inc.php配置示例
Aug 27 PHP
PHP采集静态页面并把页面css,img,js保存的方法
Dec 23 PHP
WordPress主题中添加文章列表页页码导航的PHP代码实例
Dec 22 PHP
Yii列表定义与使用分页方法小结(3种方法)
Jul 15 PHP
php curl中gzip的压缩性能测试实例分析
Nov 08 PHP
[原创]PHP global全局变量经典应用与注意事项分析【附$GLOBALS用法对比】
Jul 12 PHP
laravel5.5添加echarts实现画图功能的方法
Oct 09 PHP
laravel框架中视图的基本使用方法分析
Nov 23 PHP
PHPExcel实现的读取多工作表操作示例
Apr 14 PHP
详解PHP神奇又有用的Trait
Mar 25 #PHP
PHP自动载入类文件函数__autoload的使用方法
Mar 25 #PHP
PHP生成短网址的思路以及实现方法的详解
Mar 25 #PHP
PHP错误提示It is not safe to rely on the system……的解决方法
Mar 25 #PHP
mongodb和php的用法详解
Mar 25 #PHP
PHP随机数函数rand()与mt_rand()的讲解
Mar 25 #PHP
php微信扫码支付 php公众号支付
Mar 24 #PHP
You might like
劣质的PHP代码简化
2010/02/08 PHP
用php的ob_start来生成静态页面的方法分析
2011/03/09 PHP
php 数组使用详解 推荐
2011/06/02 PHP
深入解析PHP内存管理之谁动了我的内存
2013/06/20 PHP
PHP动态生成指定大小随机图片的方法
2016/03/25 PHP
PHP开启目录引索+fancyindex漂亮目录浏览带搜索功能
2019/09/23 PHP
EXT中xtype的含义分析
2010/01/07 Javascript
基于Jquery的将DropDownlist的选中值赋给label的实现代码
2011/05/06 Javascript
Jquery index()方法 获取相应元素索引值
2012/10/12 Javascript
js统计录入文本框中字符的个数并加以限制不超过多少
2014/05/23 Javascript
jQuery实现滚动鼠标放大缩小图片的方法(附demo源码下载)
2016/03/05 Javascript
巧用Vue.js+Vuex制作专门收藏微信公众号的app
2016/11/03 Javascript
微信小程序 setData使用方法及常用错误解决办法
2017/05/11 Javascript
利用node.js如何搭建一个简易的即时响应服务器
2017/05/28 Javascript
手把手教你搭建ES6的开发运行环境
2017/07/11 Javascript
关于javascript sort()排序你可能忽略的一点理解
2017/07/18 Javascript
详解webpack4升级指南以及从webpack3.x迁移
2018/06/12 Javascript
VUEX-action可以修改state吗
2019/11/19 Javascript
python字符串连接方法分析
2016/04/12 Python
多个应用共存的Django配置方法
2018/05/30 Python
Python爬虫获取图片并下载保存至本地的实例
2018/06/01 Python
在Python中给Nan值更改为0的方法
2018/10/30 Python
使用tqdm显示Python代码执行进度功能
2019/12/08 Python
在Pytorch中计算卷积方法的区别详解(conv2d的区别)
2020/01/03 Python
浅谈keras中的目标函数和优化函数MSE用法
2020/06/10 Python
Python如何在bool函数中取值
2020/09/21 Python
Canvas中设置width与height的问题浅析
2018/11/01 HTML / CSS
Original Penguin美国官网:布拉德皮特、强尼德普喜爱的服装品牌
2016/10/25 全球购物
美国一家著名的儿童鞋制造商:Stride Rite
2017/01/02 全球购物
大学生自荐信
2013/12/11 职场文书
销售总监岗位职责
2014/01/04 职场文书
应届毕业生求职简历自我评价
2015/03/02 职场文书
电话营销开场白
2015/05/29 职场文书
幼儿园小班教师随笔
2015/08/14 职场文书
详解Js模块化的作用原理和方案
2021/04/29 Javascript
我对PyTorch dataloader里的shuffle=True的理解
2021/05/20 Python