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

这篇文章主要介绍了PHP实现无限极分类的两种方式,结合实例形式分析了php基于递归和引用方式进行数组遍历的相关操作技巧,需要的朋友可以参考下。

本文实例讲述了PHP实现无限极分类的两种方式,分享给大家供大家参考,具体如下:

面试的时候被问到无限极分类的设计和实现,比较常见的做法是在建表的时候,增加一个PID字段用来区别自己所属的分类

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

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

递归算法

  1. /**
  2. * 递归实现无限极分类
  3. * @param $array 分类数据
  4. * @param $pid 父ID
  5. * @param $level 分类级别
  6. * @return $list 分好类的数组 直接遍历即可 $level可以用来遍历缩进
  7. */
  8. function getTree($array, $pid =0, $level = 0){
  9. //声明静态数组,避免递归调用时,多次声明导致数组覆盖
  10. static $list = [];
  11. foreach ($array as $key => $value){
  12. //第一次遍历,找到父节点为根节点的节点 也就是pid=0的节点
  13. if ($value['pid'] == $pid){
  14. //父节点为根节点的节点,级别为0,也就是第一级
  15. $value['level'] = $level;
  16. //把数组放到list中
  17. $list[] = $value;
  18. //把这个节点从数组中移除,减少后续递归消耗
  19. unset($array[$key]);
  20. //开始递归,查找父ID为该节点ID的节点,级别则为原级别+1
  21. getTree($array, $value['id'], $level+1);
  22. }
  23. }
  24. return $list;
  25. }
  26. /*
  27. * 获得递归完的数据,遍历生成分类
  28. */
  29. $array = getTree($array);
  30. foreach($array) as $value{
  31. echo str_repeat('--', $value['level']), $value['name'].'<br />';
  32. }

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

  1. 河北省
  2. --邯郸市
  3. ----永年区
  4. --武安市
  5. 北京市
  6. --朝阳区
  7. ----望京
  8. ----酒仙桥
  9. --通州区

引用算法

  1. function generateTree($array){
  2. //第一步 构造数据
  3. $items = array();
  4. foreach($array as $value){
  5. $items[$value['id']] = $value;
  6. }
  7. //第二部 遍历数据 生成树状结构
  8. $tree = array();
  9. foreach($items as $key => $value){
  10. if(isset($items[$item['pid']])){
  11. $items[$item['pid']]['son'][] = &$items[$key];
  12. }else{
  13. $tree[] = &$items[$key];
  14. }
  15. }
  16. return $tree;
  17. }
  18. //经过第一步 数据变成了这样
  19. Array
  20. (
  21. [1] => Array
  22. (
  23. [id] => 1
  24. [pid] => 0
  25. [name] => 河北省
  26. [children] => Array
  27. (
  28. )
  29. )
  30. [2] => Array
  31. (
  32. [id] => 2
  33. [pid] => 0
  34. [name] => 北京市
  35. [children] => Array
  36. (
  37. )
  38. )
  39. [3] => Array
  40. (
  41. [id] => 3
  42. [pid] => 1
  43. [name] => 邯郸市
  44. [children] => Array
  45. (
  46. )
  47. )
  48. [4] => Array
  49. (
  50. [id] => 4
  51. [pid] => 2
  52. [name] => 朝阳区
  53. [children] => Array
  54. (
  55. )
  56. )
  57. [5] => Array
  58. (
  59. [id] => 5
  60. [pid] => 2
  61. [name] => 通州区
  62. [children] => Array
  63. (
  64. )
  65. )
  66. [6] => Array
  67. (
  68. [id] => 6
  69. [pid] => 4
  70. [name] => 望京
  71. [children] => Array
  72. (
  73. )
  74. )
  75. [7] => Array
  76. (
  77. [id] => 7
  78. [pid] => 4
  79. [name] => 酒仙桥
  80. [children] => Array
  81. (
  82. )
  83. )
  84. [8] => Array
  85. (
  86. [id] => 8
  87. [pid] => 3
  88. [name] => 永年区
  89. [children] => Array
  90. (
  91. )
  92. )
  93. [9] => Array
  94. (
  95. [id] => 9
  96. [pid] => 1
  97. [name] => 武安市
  98. [children] => Array
  99. (
  100. )
  101. )
  102. )
  103. //第一步很容易就能看懂,就是构造数据,现在咱们仔细说一下第二步
  104. $tree = array();
  105. //遍历构造的数据
  106. foreach($items as $key => $value){
  107. //如果pid这个节点存在
  108. if(isset($items[$value['pid']])){
  109. //把当前的$value放到pid节点的son中 注意 这里传递的是引用 为什么呢?
  110. $items[$value['pid']]['son'][] = &$items[$key];
  111. }else{
  112. $tree[] = &$items[$key];
  113. }
  114. }

这个方法的核心在于引用,php变量默认的传值方式是按指传递

也就是说 假如说 遍历顺序是 河北省 邯郸市 当遍历到河北省时 会把河北省放到tree中 遍历到邯郸市时 会把邯郸市放到河北省的子节点数组中 但是!!! 这会儿的tree数组中 河北省已经放进去了 根据php变量按值传递的规则 你并没有更改tree数组中的河北省的数据 所以这里用到了引用传递

当你对河北省做更改时,tree数组中的河北省也一并做了更改 下面我们做个实验 我们把引用传递去掉,看一下结果

使用普通传值输出结果

  1. Array
  2. (
  3. [0] => Array
  4. (
  5. [id] => 1
  6. [pid] => 0
  7. [name] => 河北省
  8. )
  9. [1] => Array
  10. (
  11. [id] => 2
  12. [pid] => 0
  13. [name] => 北京市
  14. )
  15. )

可以看到 只有河北省和北京市输出出来了 因为他们俩是第一级节点 而且排行1和2,放到$tree数组中之后,没有使用引用传递,那么后续对他俩的子节点的操作都没有在$tree中生效,现在我们更改一下顺序 把邯郸市放到河北省的前面 那么根据咱们的推断 那么邯郸市就应该出现在tree数组里

邯郸市放到河北省前面的输出结果

  1. Array
  2. (
  3. [0] => Array
  4. (
  5. [id] => 1
  6. [pid] => 0
  7. [name] => 河北省
  8. [son] => Array
  9. (
  10. [0] => Array
  11. (
  12. [id] => 3
  13. [pid] => 1
  14. [name] => 邯郸市
  15. )
  16. )
  17. )
  18. [1] => Array
  19. (
  20. [id] => 2
  21. [pid] => 0
  22. [name] => 北京市
  23. )
  24. )

果然是这样 那么证明我们的推断是正确的 现在我们把引用传值改回去 再看一下

使用引用传值输出结果

  1. Array
  2. (
  3. [1] => Array
  4. (
  5. [id] => 1
  6. [pid] => 0
  7. [name] => 河北省
  8. [children] => Array
  9. (
  10. [0] => Array
  11. (
  12. [id] => 3
  13. [pid] => 1
  14. [name] => 邯郸市
  15. [children] => Array
  16. (
  17. [0] => Array
  18. (
  19. [id] => 8
  20. [pid] => 3
  21. [name] => 永年区
  22. )
  23. )
  24. )
  25. [1] => Array
  26. (
  27. [id] => 9
  28. [pid] => 1
  29. [name] => 武安市
  30. )
  31. )
  32. )
  33. [2] => Array
  34. (
  35. [id] => 2
  36. [pid] => 0
  37. [name] => 北京市
  38. [children] => Array
  39. (
  40. [0] => Array
  41. (
  42. [id] => 4
  43. [pid] => 2
  44. [name] => 朝阳区
  45. [children] => Array
  46. (
  47. [0] => Array
  48. (
  49. [id] => 6
  50. [pid] => 4
  51. [name] => 望京
  52. )
  53. [1] => Array
  54. (
  55. [id] => 7
  56. [pid] => 4
  57. [name] => 酒仙桥
  58. )
  59. )
  60. )
  61. [1] => Array
  62. (
  63. [id] => 5
  64. [pid] => 2
  65. [name] => 通州区
  66. )
  67. )
  68. )
  69. )

树状结构完美的输出出来了 这个方法的核心就是引用传值。