简述论坛中无限分类的实现原理。

PHP实现无限极分类的方式
假如你面试的时候被问到无限极分类的设计和实现,常见的方法是在建表或者数据的时候,增加一个[pid或者parent_id]字段用来区别自己所属的分类
譬如
数据表设计模板参考

id(int)pid(int)name(varchar)
101级
201级
312级

select出来的数据就是如下,怎么实现无限极递归呢,有两种常用的做法,递归引用算法

$array = array(
    array('id' => 1, 'pid' => 0, 'name' => '1级'),
    array('id' => 2, 'pid' => 0, 'name' => '1级'),
    array('id' => 3, 'pid' => 1, 'name' => '2级'),
    array('id' => 4, 'pid' => 2, 'name' => '2级'),
    array('id' => 5, 'pid' => 2, 'name' => '2级'),
    array('id' => 6, 'pid' => 4, 'name' => '3级'),
    array('id' => 7, 'pid' => 4, 'name' => '3级'),
    array('id' => 8, 'pid' => 3, 'name' => '3级'),
    array('id' => 9, 'pid' => 1, 'name' => '2级'),
    array('id' => 10, 'pid' => 8, 'name' => '4级'),
    array('id' => 11, 'pid' => 10, 'name' => '5级'),
    array('id' => 12, 'pid' => 11, 'name' => '6级'),
    //...
);

递归算法

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;
}
/**************    调用递归方法后遍历新数组   ****************/
$tree1 = getTree($array);
foreach($tree1 as $val){
    echo str_repeat('--', $val['level']).$val['name'].'<br />';
}

得到的数据如下

Array
(
    [0] => Array
        (
            [id] => 1
            [pid] => 0
            [name] => 1级
            [level] => 0
        )

    [1] => Array
        (
            [id] => 3
            [pid] => 1
            [name] => 2级
            [level] => 1
        )

    [2] => Array
        (
            [id] => 8
            [pid] => 3
            [name] => 3级
            [level] => 2
        )

    [3] => Array
        (
            [id] => 10
            [pid] => 8
            [name] => 4级
            [level] => 3
        )

    [4] => Array
        (
            [id] => 11
            [pid] => 10
            [name] => 5级
            [level] => 4
        )

    [5] => Array
        (
            [id] => 12
            [pid] => 11
            [name] => 6级
            [level] => 5
        )

    [6] => Array
        (
            [id] => 9
            [pid] => 1
            [name] => 2级
            [level] => 1
        )

    [7] => Array
        (
            [id] => 2
            [pid] => 0
            [name] => 1级
            [level] => 0
        )

    [8] => Array
        (
            [id] => 4
            [pid] => 2
            [name] => 2级
            [level] => 1
        )

    [9] => Array
        (
            [id] => 6
            [pid] => 4
            [name] => 3级
            [level] => 2
        )

    [10] => Array
        (
            [id] => 7
            [pid] => 4
            [name] => 3级
            [level] => 2
        )

    [11] => Array
        (
            [id] => 5
            [pid] => 2
            [name] => 2级
            [level] => 1
        )

)

遍历后的结果如下

1级
–2级
----3级
------4级
--------5级
----------6级
–2级
1级
–2级
----3级
----3级
–2级

接下来是递归算法

//引用算法
function generateTree($array){
    //第一步 构造数据
    $items = array();
    foreach($array as $value){
        $items[$value['id']] = $value;
    }
    //第二步 遍历数据 生成树状结构
    $tree = array();
    foreach($items as $key => $value){
        if(isset($items[$value['pid']])){
            //存在父级
            /**************    引用了$items[$value['pid']] 所以$items[$value['pid']]改变,$tree的关联的键元素也会发生改变      ****************/
            /**************    看不懂这一步的建议去了解一下引用的原理     ****************/
            $items[$value['pid']]['children'][] = &$items[$key];
        }else{
            //不存在父级的顶级元素才会走到这一步
            /**************    看不懂这一步的建议去了解一下引用的原理     ****************/
            $tree[] = &$items[$key];
        }
    }
    return $tree;
}
$tree2 = generateTree($array);

得到的数据如下

Array
(
    [0] => Array
        (
            [id] => 1
            [pid] => 0
            [name] => 1[children] => Array
                (
                    [0] => Array
                        (
                            [id] => 3
                            [pid] => 1
                            [name] => 2[children] => Array
                                (
                                    [0] => Array
                                        (
                                            [id] => 8
                                            [pid] => 3
                                            [name] => 3[children] => Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [id] => 10
                                                            [pid] => 8
                                                            [name] => 4[children] => Array
                                                                (
                                                                    [0] => Array
                                                                        (
                                                                            [id] => 11
                                                                            [pid] => 10
                                                                            [name] => 5[children] => Array
                                                                                (
                                                                                    [0] => Array
                                                                                        (
                                                                                            [id] => 12
                                                                                            [pid] => 11
                                                                                            [name] => 6)

                                                                                )

                                                                        )

                                                                )

                                                        )

                                                )

                                        )

                                )

                        )

                    [1] => Array
                        (
                            [id] => 9
                            [pid] => 1
                            [name] => 2)

                )

        )

    [1] => Array
        (
            [id] => 2
            [pid] => 0
            [name] => 1[children] => Array
                (
                    [0] => Array
                        (
                            [id] => 4
                            [pid] => 2
                            [name] => 2[children] => Array
                                (
                                    [0] => Array
                                        (
                                            [id] => 6
                                            [pid] => 4
                                            [name] => 3)

                                    [1] => Array
                                        (
                                            [id] => 7
                                            [pid] => 4
                                            [name] => 3)

                                )

                        )

                    [1] => Array
                        (
                            [id] => 5
                            [pid] => 2
                            [name] => 2)

                )

        )

)

引用算法的遍历方法如下

function ergoTree($arr,$level=0){
    foreach ($arr as $item){
        if(isset($item['children'])){
            echo str_repeat('--', $level).$item['name']."<br>";
            ergoTree($item['children'],$level+1);
        }else{
            echo str_repeat('--', $level).$item['name']."<br>";
        }
    }
}
ergoTree($tree2);

得到相同的结果

1级
–2级
----3级
------4级
--------5级
----------6级
–2级
1级
–2级
----3级
----3级
–2级

原理分析
通过第一次遍历$items后得到的是以下的数据

Array
(
    [1] => Array
        (
            [id] => 1
            [pid] => 0
            [name] => 1级
        )

    [2] => Array
        (
            [id] => 2
            [pid] => 0
            [name] => 1级
        )

    [3] => Array
        (
            [id] => 3
            [pid] => 1
            [name] => 2级
        )

    [4] => Array
        (
            [id] => 4
            [pid] => 2
            [name] => 2级
        )

    [5] => Array
        (
            [id] => 5
            [pid] => 2
            [name] => 2级
        )

    [6] => Array
        (
            [id] => 6
            [pid] => 4
            [name] => 3级
        )

    [7] => Array
        (
            [id] => 7
            [pid] => 4
            [name] => 3级
        )

    [8] => Array
        (
            [id] => 8
            [pid] => 3
            [name] => 3级
        )

    [9] => Array
        (
            [id] => 9
            [pid] => 1
            [name] => 2级
        )

    [10] => Array
        (
            [id] => 10
            [pid] => 8
            [name] => 4级
        )

    [11] => Array
        (
            [id] => 11
            [pid] => 10
            [name] => 5级
        )

    [12] => Array
        (
            [id] => 12
            [pid] => 11
            [name] => 6级
        )

)

然后再将这组新的数组进行第二次遍历

遍历时走到第一个元素后判断到该元素的$key['pid']并不存在$items中,所以$tree[]直接引用$items[$key],同理第二个也是一样为一级分类。
遍历走到第三个的时候判断到该元素的pid是$items[1]的子元素,那么直接将第三个元素的数据引用到$items[1]['children']中。
那么此时的$items$tree会变成如下
//这是$items的
Array
(
    [1] => Array
        (
            [id] => 1
            [pid] => 0
            [name] => 1级
            [children] => Array
                (
                    [0] => Array
                        (
                            [id] => 3
                            [pid] => 1
                            [name] => 2级
                        )

                )

        )

    [2] => Array
        (
            [id] => 2
            [pid] => 0
            [name] => 1级
        )

    [3] => Array
        (
            [id] => 3
            [pid] => 1
            [name] => 2级
        )
    ...
   
//这是$tree的
Array
(
    [0] => Array
        (
            [id] => 1
            [pid] => 0
            [name] => 1级
            [children] => Array
                (
                    [0] => Array
                        (
                            [id] => 3
                            [pid] => 1
                            [name] => 2级
                        )

                )

        )

    [1] => Array
        (
            [id] => 2
            [pid] => 0
            [name] => 1级
        )

)
/*
 * 为什么这儿的$tree[0][1]的元素会变成同$items[1]的会变成一样的,去了解一下 & 引用你就明白了 
 * 同理,$items[1]['children'][]引用了$items[$key],就算$items[$key]同样有子元素,只要$items[$key]添加['children']数据内容改变,$items[1]['children'][]内的数据会跟着发生改变!
 * */
Array
(
    [1] => Array
        (
            [id] => 1
            [pid] => 0
            [name] => 1级
            [children] => Array
                (
                    [0] => Array
                        (
                            [id] => 3
                            [pid] => 1
                            [name] => 2级
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [id] => 8
                                            [pid] => 3
                                            [name] => 3级
                                        )

                                )

                        )
...          
 [3] => Array
        (
            [id] => 3
            [pid] => 1
            [name] => 2级
            [children] => Array
                (
                    [0] => Array
                        (
                            [id] => 8
                            [pid] => 3
                            [name] => 3级
                        )

                )

        )
   
//对比数据就能理解了

写得不好,纯手打编辑,欢迎纠错!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值