无限极分类递归,引用,栈三种方法的实现,并根据分类id倒回去查找所有所属父级分类信息

最近做的一个项目涉及到无限分类的问题,现在总结如下,代码使用PHP语言实现,因为使用了TP3的框架,所以一些方法的使用上,直接使用了TP3现有的方法。

想要根据分类id查找所有所属父级分类信息请往后拉,这部分在最后实现!!!

建表:

涉及无限极分类的问题建表时,都会有一个专门的分类表,表中有一个pid字段默认为0,属于一级分类,pid字段关联当前分类表的id,用来标明当前分类所属的上级分类,例如建一个问题分类表的语句如下:

CREATE TABLE `quescate` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '问题分类id',
  `title` varchar(250) NOT NULL COMMENT '问题分类标题',
  `pid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所属问题id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='问题分类表';

一、无限极分类的实现

接下来取出各级分类及分类下的子分类,自己做时用了最笨的办法:

(1)先查找一级分类(pid=0)

(2)通过上级分类查找的id作为pid,来查找下一级分类

(3)重复步骤(2)

代码实现如下:

where('pid = 0')->select();
    if (!empty($cate_0)){
        // 二级分类
        $ids_1 = array_map('reset', $cate_0); // 使用array_column()更简单,但是需要>=PHP5.5.0的版本
        $map_1['pid'] = array('in', $ids_1);
        $cate_1 = $this->where($map_1)->select();
        if(!empty($cate_1)){
            // 三级分类
            $ids_2 = array_map('reset', $cate_1);
            $map_2['pid'] = array('in', $ids_2);
            $cate_2 = $this->where($map_2)->select();
        }
        $cate = array(0=>$cate_0, 1=>$cate_1, 2=>$cate_2);
    }
    unset($ids_1, $map_1, $ids_2, $map_2);

    return $cate;
?>

但是这样的办法,有几级分类就要查找几次,太过麻烦,也会造成代码冗余。根据上述步骤很轻易看出,这其实就是一个递归。于是在优化代码的过程中,可以通过递归实现无限极分类,通过查找资料还找到了引用,栈的实现方法,下面一一介绍,为了更直观,使用的数据如下:

 8, 'title' => '美国', 'pid' => 0),
        array('id' => 9, 'title' => '纽约', 'pid' => 8),
        array('id' => 10, 'title' => '韩国', 'pid' => 0),
        array('id' => 11, 'title' => '日本', 'pid' => 0),
        array('id' => 12, 'title' => '加拿大', 'pid' => 0),
        array('id' => 13, 'title' => '中国', 'pid' => 0),
        array('id' => 14, 'title' => '华盛顿', 'pid' => 8),
        array('id' => 16, 'title' => '洛杉矶', 'pid' => 8),
        array('id' => 17, 'title' => '北京', 'pid' => 13),
        array('id' => 18, 'title' => '上海', 'pid' => 13),
        array('id' => 19, 'title' => '广东', 'pid' => 13),
        array('id' => 20, 'title' => '深圳', 'pid' => 13),
        array('id' => 21, 'title' => '杭州', 'pid' => 13)
    );
?>
1. 递归,实现无限极分类。递归应该是最容易想到的方法了。其实递归函数也是借助于栈的机制实现的,但是底层对于栈的处理对于程序员来说都是透明的,程序员只需要关心应用的实现逻辑。所以说使用递归处理上述问题理解起来比较容易,代码也比较简洁。在我看来,最容易理解的方法应该是使用栈的方式实现无限极分类了(可以参考以下的第三种方法)
主要参考博客:
php实现无限级分类查询(递归、非递归)中开头部分递归与栈的讲解
PHP实现无限极分类的两种方式,递归和引用中的递归方法
 $value){
            //第一次遍历,找到父节点为根节点的节点 也就是pid=0的节点
            if ($value['pid'] == $pid){
                //父节点为根节点的节点,级别为0,也就是第一级
                $value['level'] = $level;
                //把数组放到list中
                $list[] = $value;
                //把这个节点从数组中移除,减少后续递归消耗
                unset($array[$key]);
                //开始递归,查找父ID为该节点ID的节点,级别则为原级别+1
                $this->getTree($array, $value['id'], $level+1);
            }
        }
        return $list;
    }

    // 无限极分类的第一种方法:递归
    $list = getTree($res, 0, 1);
    /*
    * 以下是根据自己需要对分好类的数据进行的处理,这里为了保持与上边需要的数据形式一致
    */
    foreach ($list as $key=>$value){
        if ($value['level'] == 1){
            $cate[0][] = $value; // 一级分类
        }elseif ($value['level'] == 2){
            $cate[1][] = $value; // 二级分类
        }
    }

    return $cate;
?>

2. 引用,实现无限极分类。此方法非常巧用引用。如果不了解引用,对下面方法的理解是困难的,建议参考PHP手册引用一章

主要参考博客:PHP无限极分类实现中的方法一,里面的方法二只是判断将三目运算符改成了if判断,喜欢的可以看看

$item) {
            $items[$item['pid']]['son'][$key] = &$items[$key];
        }
        return isset($items[0]['son']) ? $items[0]['son'] : array();
    }

    $cate[1] = array(); // 预定义二级分类为空
    // 将数组索引改为id值
    $res = array_reduce($res, create_function('$res, $val', '$res[$val["id"]] = $val; return $res;'));
    $list = $this->arrayToTree($res);
    /*
    * 以下是根据自己需要对分好类的数据进行的处理,这里为了保持与上边需要的数据形式一致
    */
    foreach ($list as $ke=>$val){
        if (isset($val['son'])){
            $cate[1] += $val['son']; // 二级菜单
            unset($val['son']); // 从二级菜单中去除一级菜单
        }
        $cate[0][] = $val; // 一级菜单
    }

    return $cate;
?>

3. 栈,实现无限极分类。栈的实现方式应该算是最容易理解的了,当然栈的核心:“先进后出”是必须要知道才能理解好下面的方法的。有关于栈,有兴趣的可以查一查《数据结构(C语言版)》中对于栈的讲解。

主要参考博客:php实现无限级分类查询(递归、非递归)中的“非递归,即使用栈机制实现无限级栏目的查询”

 $val) {
            if ($val['pid'] == 0)
                pushStack($stack, $val, 1);
        }
        /*
         * 将栈中的元素出栈,查找其子栏目
         * */
        do {
            $par = popStack($stack); //将栈顶元素出栈
            /*
             * 查找以此栏目为父级栏目的id,将这些栏目入栈
             * */
            foreach ($array as $key=>$value){
                if ($value['pid'] == $par['id']){
                    pushStack($stack, $value, $par['dep']+1);
                }
            }
            // 将出栈的栏目以及该栏目的深度保存到数组中
            $list[] = $par;
        } while (count($stack) > 0);

        return $list;
    }

    //$res = $this->order('id desc')->select(); // 因为栈先进后出的特点,分类完后的数据会是原数据的倒序排序。这可以在处理数据前,将数据先倒序输出,这样得到的数据就是正序的。
    $res = array_reverse($res);
    $list = getStack($res);
    foreach ($list as $key=>$value){
        if ($value['dep'] == 1){
            $cate[0][] = $value; // 一级分类
        }elseif ($value['dep'] == 2){
            $cate[1][] = $value; // 二级分类
        }
    }

    return $cate;
?>

总结:通过以上三种方法可以发现:递归会是最容易想到也最常用到的一种方式,比较容易理解使用方便;引用使用非常巧妙而且代码简洁但是不太好理解;栈的实现最容易理解,但要理解并注意栈的特点:先进后出。

二、根据分类id倒着查找所有的所属父分类信息

本来想将这部分放在下一篇博客讲解的,但想趁热打铁,索性放在一篇博客里总结完。

项目中有时候需要根据分类的id倒回去查找所有所属的父级分类信息,比如面包屑,前端菜单栏里只允许展示有具体信息的分类时,我们就要涉及到这个查找。同样的这个查找也需要用到递归的实现。下面是两种递归的实现方法,但大同小异,基本原理都一样。

主要参考博文:php实现无限极分类中的“根据子类id查找出所有父级分类信息”

1. 方法一:Yii2框架中的方法

0){
                    get_parent_list($arr,$u['pid']);

                }
            }
        }
        return $list;
    }

    $cate = get_parent_list($res, $pid); // 此处$pid根据查到的具体分类信息中的pid传值查询所属上级分类
    $cate = array_reverse($cate); // 此处根据自己需要做处理,我这里为了页面展示方便做了数组倒序处理
?>

2. 方法二:递归查询分类信息,与方法一大同小异

where('id = '.$id)->find(); // 此处就是查询分类表中一条语句

        if($cate){
            $list[] = $cate;
            $id = $cate['pid'];
            if($cate['pid'] > 0){
                self::get_parents($id);
            }
        }

        return $list;
    }

    $cate = get_parents($pid); // 根据需要查要查询分级分类的分类id
    $cate = array_reverse($cate); // 此处我是根据自己前端展示分方便最数组做了倒序处理
?>

 

你可能感兴趣的:(技术,PHP)