我們知道數據庫一般是以一個列表(id,pid)的形式保存樹的。如何提取這棵樹呢?最簡單的方法就是根據pid循環查表。但是毫無疑問,這會產生巨大的數據庫查詢開銷。
那麼一般建議的方法是一次性將全部相關數據全查出來,但是這就涉及到一個問題,如何快速的構建一棵樹。
我曾經一直以為,這是一個復雜的操作,至少需要一個遞歸,時間復雜度不會是O(n)。
前段時間,一個工作上的需求,需要解決這個問題。我仔細想了想,發現完全可以通過單層循環解決這個問題,實現如下:
1 function list2Tree($listItem, $idx = 'id', $pIdx = 'pid', $childKey= 'list'){ 2 $map = array(); 3 $pMap = array(); 4 5 foreach($listItem as $item){ 6 $id = $item[$idx]; 7 $pid = $item[$pIdx]; 8 $map[$id] = &$item; 9 unset($item); 10 } 11 12 foreach($map as $id => &$item){ 13 $pid = $item[$pIdx]; 14 $item[$childKey] = array(); 15 16 if(! isset($map[$pid])){ 17 $pMap[$id] = &$item; 18 } 19 else{ 20 $pItem= &$map[$pid]; 21 $pItem[$childKey][] = &$item; 22 } 23 24 unset($item, $pItem); 25 } 26 27 return array_shift($pMap); 28 }
測試一下:
1 // 路徑方便識別父子關系 2 $json = <<<JSON 3 [ 4 { 5 "id": 2, 6 "pid": 1, 7 "path": "/se" 8 }, 9 { 10 "id": 3, 11 "pid": 2, 12 "path": "/se/4901" 13 }, 14 { 15 "id": 4, 16 "pid": 5, 17 "path": "/se/4901/mask/query" 18 }, 19 { 20 "id": 5, 21 "pid": 3, 22 "path": "/se/4901/mask" 23 }, 24 { 25 "id": 6, 26 "pid": 2, 27 "path": "/se/4902" 28 }, 29 { 30 "id": 7, 31 "pid": 6, 32 "path": "/se/4902/mask" 33 } 34 ] 35 JSON; 36 37 $list = json_decode($json, true); 38 39 var_dump(list2Tree($list));
結果:
array(4) { ["id"]=> int(2) ["pid"]=> int(1) ["path"]=> string(3) "/se" ["list"]=> array(2) { [0]=> array(4) { ["id"]=> int(3) ["pid"]=> int(2) ["path"]=> string(8) "/se/4901" ["list"]=> array(1) { [0]=> array(4) { ["id"]=> int(5) ["pid"]=> int(3) ["path"]=> string(13) "/se/4901/mask" ["list"]=> array(0) { } } } } [1]=> array(4) { ["id"]=> int(6) ["pid"]=> int(2) ["path"]=> string(8) "/se/4902" ["list"]=> array(1) { [0]=> array(4) { ["id"]=> int(7) ["pid"]=> int(6) ["path"]=> string(13) "/se/4902/mask" ["list"]=> array(0) { } } } } } }
成功把列表轉成了樹