1. 程式人生 > >PHP實現無限極分類的兩種方式,遞迴和引用

PHP實現無限極分類的兩種方式,遞迴和引用

https://blog.csdn.net/falcom_fans/article/details/75579663

說到無限極分類,比較常見的做法是在建表的時候,增加一個PID欄位用來區別自己所屬的分類 這裡寫圖片描述  由於展示資料的時候,需要表達出這種所屬關係,所以必然要在讀取資料的時候進行一系列處理,由此就牽涉到了兩種演算法

國民級演算法——遞迴

從資料庫取得二維陣列省略,遞迴的思路其實很簡單,遍歷陣列,根據每條資料的id值去尋找所有pid值等於自己id值的資料,直到找不到為止,實際實現起來也是通俗易懂

function getTree($arr,$pid=0,$level=0)
{
    static $list = [];
    foreach ($arr as $key => $value) {
        if ($value["auth_pid"] == $pid) {
            $value["level"] = $level;
            $list[] = $value;
            unset($arr[$key]); //刪除已經排好的資料為了減少遍歷的次數,當然遞迴本身就很費神就是了
            getTree($arr,$value["id"],$level+1);
        }
    }
    return $list;
}

基本也沒啥好說的,這裡返回去的是一個已經排序好的一維陣列,展示的時候直接遍歷就好,加入level欄位是為了展示的時候,如果需要縮排,可以有個依據

巧妙的引用演算法

上面的遞迴原理通俗易懂,但是總所周知的原因,遞迴對資源的消耗是非常大的,實際執行起來效率也很低,所以有了下面的通過引用演算法

function generateTree($data){
    $items = array();
    foreach($data as $v){
        $items[$v['auth_id']] = $v;
    }
    $tree = array();
    foreach($items as $k => $item){
        if(isset($items[$item['auth_pid']])){
            $items[$item['auth_pid']]['son'][] = &$items[$k];
        }else{
            $tree[] = &$items[$k];
        }
    }
    return $tree;
}

是不是感覺有點謎之暈,慢慢分析這一段程式碼  整個方法大體分成兩個部分  第一部分是

   $items = array();
    foreach($data as $v){
        $items[$v['auth_id']] = $v;
    }

這一段應該是很通俗的,就是構建一個新的陣列,新陣列的key值是自己的主鍵id值  進行完這一步之後,應該得到的陣列形式是這樣額

Array
(
    [100] => Array
        (
            [auth_id] => 100
            [auth_name] => 後臺首頁
            [auth_pid] => 0
        )

    [116] => Array
        (
            [auth_id] => 116
            [auth_name] => 管理員
            [auth_pid] => 0
        )

    [120] => Array
        (
            [auth_id] => 120
            [auth_name] => 管理員列表
            [auth_pid] => 116
        )

    [121] => Array
        (
            [auth_id] => 121
            [auth_name] => 管理員新增
            [auth_pid] => 116
        )

    [122] => Array
        (
            [auth_id] => 122
            [auth_name] => 資料一覽
            [auth_pid] => 100
        )

    [123] => Array
        (
            [auth_id] => 123
            [auth_name] => 更新日誌
            [auth_pid] => 100
        )
)

至於為什麼要特地多一次遍歷來將陣列的KEY值重構,這裡就是第二部分的巧妙之處了

 $tree = array();
    foreach($items as $k => $item){
        if(isset($items[$item['auth_pid']])){
            $items[$item['auth_pid']]['son'][] = &$items[$k];
        }else{
            $tree[] = &$items[$k];
        }
    }
    return $tree;

慢慢的分析一下,這段程式碼將已經重構的陣列遍歷  並判斷當前陣列元素的父級分類是否存在  舉個例子 foreach第一次迴圈的時候

$k = 100;
$item = Array
        (
            [auth_id] => 100
            [auth_name] => 後臺首頁
            [auth_pid] => 0
        )
 $items[$item['auth_pid']] = $items[0]; //不存在鍵值為0的陣列元素,證明是頂級分類
 isset($items[$item['auth_pid']]) = false;
 $tree[] = &$items[$k];

注意到這裡,是採取引用的方式,為什麼呢?因為後面,其實我們的陣列元素是會變化的  當foreach第三次迴圈的時候,同樣分析

$k = 120;
$item = Array
        (
            [auth_id] => 120
            [auth_name] => 管理員列表
            [auth_pid] => 116
        )
 $items[$item['auth_pid']] = $items[116];
 isset($items[$item['auth_pid']]) = true; //存在鍵值為116的陣列元素,證明這個元素是鍵值116元素的子分類
 $items[$item['auth_pid']]['son'][] = &$items[$k];//給鍵值為116的陣列元素增加一個son鍵,並將當前遍歷的這個元素賦值給這個鍵

這裡也是採取了引用,還是那個原因,因為當前遍歷的元素很有可能還有子分類,當有子分類的時候,按照這個演算法,他自己還要增加son這個鍵,所以採用引用賦值的方式,可以保證自己的結構是完美的

整體來看這個演算法,如果加上兩句輸出來看一下

 $tree = array();
    foreach($items as $k => $item){
        if(isset($items[$item['auth_pid']])){
            $items[$item['auth_pid']]['son'][] = &$items[$k];
            echo "1111<br>";
        }else{
            echo "2222<br>";
            $tree[] = &$items[$k];
        }
    }

那麼會得到這樣的結果

2222
2222
1111
1111
1111
1111

可以看到,其實$tree裡面只有兩個陣列元素,而這兩個陣列元素是帶有son鍵的,在son鍵裡面儲存著自己的所有後代元素  進行完這個演算法之後,得到的結果會是這樣的


Array
(
    [0] => Array
        (
            [auth_id] => 100
            [auth_name] => 後臺首頁
            [auth_pid] => 0
            [son] => Array
                (
                    [0] => Array
                        (
                            [auth_id] => 122
                            [auth_name] => 資料一覽
                            [auth_pid] => 100
                        )
                    [1] => Array
                        (
                            [auth_id] => 123
                            [auth_name] => 更新日誌
                            [auth_pid] => 100
                        )
                )
        )
    [1] => Array
        (
            [auth_id] => 116
            [auth_name] => 管理員
            [auth_pid] => 0
            [son] => Array
                (
                    [0] => Array
                        (
                            [auth_id] => 120
                            [auth_name] => 管理員列表
                            [auth_pid] => 116
                        )
                    [1] => Array
                        (
                            [auth_id] => 121
                            [auth_name] => 管理員新增
                            [auth_pid] => 116
                        )
                )
        )
)

這個結果可以很方便的採用json的方式返回給前臺,或者接下來採用遞迴的方式使其變成一維陣列,都是很方便的,整個方法的精妙處就在於引用的使用

雖然這個例子使用的只有二級分類,實際上無論幾級分類都是很完美的,而且在執行速度上可以說都是很快的,從時間複雜度來說只是一個for迴圈,比遞迴不知道高到哪裡去了!