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] => 通州區
                        )

                )

        )

)
//樹狀結構完美的輸出出來了 這個方法的核心就是引用傳值

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM