平常開發中,咱們會碰到構造樹的需求,經過id,pid的關係去構建一個樹結構,而後對樹進行遍歷等操做。其實現方式分爲兩種: 1. 遞歸, 2. 引用
而這兩個方法的優缺點也很明顯。php
寫本文的原由是,這幾天碰到很是好的一個解決方案,讓我從新理解了引用。經過本文,總結下本身的學習成果.ok,那直接上代碼了。函數
若是下面的代碼,你看完就能理解了,說明你引用真是學到家了, 你也能夠直接跳過本文哈~。
function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children") { $tmp = []; //以id爲健,$value爲值的容器,能夠很巧妙的判斷根節點元素 $tree = []; //利用引用,對$data的數據進行操做 foreach ($data as $key => &$value) { // $tmp[$value['id']] = &$value; if (!isset($tmp[$value['pid']])) { $tree[] = &$tmp[$value['id']]; }else { $temp = &$tmp[$value['pid']]; $temp[$child][] = &$value; } unset($temp, $value); } return $tree; }
ok,先不說其餘的,你先拿下面的數據測試下這個方法.性能
$data= [ ["id" => 1, "pid" => 0 , "name" => 'Universe'], ["id" => 2, "pid" => 1 , "name" => 'Earth'], ["id" => 3, "pid" => 2 , "name" => 'China'], ["id" => 4, "pid" => 3 , "name" => 'Beijing'], ];
補充:這個方法須要注意一點,須要父節點在前,不適合無序數據,因此若是是無序的,先得排序.
若是沒有意外,打印的結果,應該以下:學習
array(1) { [0]=> array(4) { ["id"]=> int(1) ["pid"]=> int(0) ["name"]=> string(8) "Universe" ["children"]=> array(1) { [0]=> array(4) { ["id"]=> int(2) ["pid"]=> int(1) ["name"]=> string(5) "Earth" ["children"]=> array(1) { [0]=> array(4) { ["id"]=> int(3) ["pid"]=> int(2) ["name"]=> string(5) "China" ["children"]=> array(1) { [0]=> array(3) { ["id"]=> int(4) ["pid"]=> int(3) ["name"]=> string(7) "Beijing" } } } } } } } }
若是到此,你還想不明白,不要緊,咱們一一來分析下.
其實要完全弄明白這個解決方案,須要理解二個部分。測試
foreach
$data = ["student", "teacher"]; foreach ($data as $index => $item) { }
注意每次循環的時候, 是把$data[0]和$data[1] 的「值」複製一份 再賦給 $itemui
引用( 必定要本身動手試驗下)
$a = 1; $b = &$a; $c = $b; $c = 2; 猜猜看 $b = ?;
若是引用有疑問,點我.net
到此,若是你能理解上面foreach和引用,而且能理解這個解決方案的全部執行過程,那麼恭喜你,你學的真好! 但若是仍是有困難,不要緊,我們一步一步踏踏實實的來.code
ok,深吸一口氣,跟着個人思路,我們一步一步來.排序
- 首先我們看下原函數
function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children") { $tmp = []; #以id爲健,$value爲值的容器,能夠很巧妙的判斷根節點元素 $tree = []; #利用引用,對$data的數據進行操做 foreach ($data as $key => &$value) { #&$value取到$data元素對應值的引用 $tmp[$value['id']] = &$value; #以$value['id']爲鍵,&$value引用爲值push到$tmp中, #這樣能夠巧妙的判斷當前元素是否爲根節點 if (!isset($tmp[$value['pid']])) { #將根節點push到$tree中 $tree[] = &$tmp[$value['id']]; }else { #若當前元素的父節點存在於$tmp中, 引用獲取$tmp中對應父節點的值 $temp = &$tmp[$value['pid']]; #而後將當前元素push到其父節點的children中 $temp[$child][] = &$value; } #爲了避免引發變量污染, 引用用完後,須要unset掉 unset($temp, $value); } return $tree; }
function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children") { # $tmp = []; # $tree = []; # foreach ($data as $key => &$value) { // $tmp[$value['id']] = &$value; if (!isset($tmp[$value['pid']])) { $tree[] = &$tmp[$value['id']]; }else { # $temp = &$tmp[$value['pid']]; # $temp[$child][] = &$value; # } unset($temp, $value); } return $tree; }
變量狀況:
$data[0] = ["id" => 1, "pid" => 0 , "name" => 'Universe'];
$tmp[1] = &$data[0];
$tree[] = &$data[0]遞歸
function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children") { # $tmp = []; # $tree = []; # foreach ($data as $key => &$value) { // $tmp[$value['id']] = &$value; # if (!isset($tmp[$value['pid']])) { # $tree[] = &$tmp[$value['id']]; }else { $temp = &$tmp[$value['pid']]; $temp[$child][] = &$value; } unset($temp, $value); } return $tree; }
變量狀況:
$data[1] = ["id" => 2, "pid" => 1 , "name" => 'Earth'];
$value=&$data[1];
$tmp[2] = &$data[1];
注意:
$temp即&$tmp[1],即和$data[0]指向相同的地址
因此$temp['children'][] = &$value ,操做的結果是:
$data[ [ "id" => 1, "pid" => 0 , "name" => 'Universe' "children"=>[ &$data[1], //注意:存儲的是引用 ] ] ... ]
4.第三次循環
function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children") { # $tmp = []; # $tree = []; # foreach ($data as $key => &$value) { // $tmp[$value['id']] = &$value; # if (!isset($tmp[$value['pid']])) { # $tree[] = &$tmp[$value['id']]; }else { $temp = &$tmp[$value['pid']]; $temp[$child][] = &$value; } unset($temp, $value); } return $tree; }
變量狀況:
$data[2] = ["id" => 3, "pid" => 2 , "name" => 'China'];
$value = &$data[2];
$tmp[3] = &$data[2];
注意:
$temp即&$tmp[2],即和$data[1]指向相同的地址
因此$temp['children'][] = &$value ,操做的結果是:
這裏注意一下:
這是第二次循環的時候,children中存儲的$data[1]的引用
$data[ [ "id" => 1, "pid" => 0 , "name" => 'Universe' "children"=>[ &$data[1], //注意:存儲的是引用 ] ] ... ]
第三次循環的的時候,則是$data[1]['children'][] = &$value, 而$value指向的是$data[2]
,因此結果是:
$data[ [ "id" => 1, "pid" => 0 , "name" => 'Universe' "children"=>[ // &$data[1], //注意:存儲的是引用 [ "id" => 2, "pid" => 1 , "name" => 'Earth' "children" => [ &data[2] //注意:存儲的是引用 ] ] ] ] ] ... ]
5.第四次循環
function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children") { # $tmp = []; # $tree = []; # foreach ($data as $key => &$value) { // $tmp[$value['id']] = &$value; # if (!isset($tmp[$value['pid']])) { # $tree[] = &$tmp[$value['id']]; }else { $temp = &$tmp[$value['pid']]; $temp[$child][] = &$value; } unset($temp, $value); } return $tree; }
變量狀況:
$data[3] = ["id" => 4, "pid" => 3 , "name" => 'Beijing'];
$value = &$data[3];
$tmp[3] = &$data[3];
注意:
$temp即&$tmp[2],即和$data[1]指向相同的地址
因此$temp['children'][] = &$value ,操做的結果是:
這裏注意一下:
這是第三次循環的時候,children中存儲的$data[2]的引用
$data[ [ "id" => 1, "pid" => 0 , "name" => 'Universe' "children"=>[ // &$data[1], //注意:存儲的是引用 [ "id" => 2, "pid" => 1 , "name" => 'Earth' "children" => [ &data[2] //注意:存儲的是引用 ] ] ] ] ] ... ]
第四次循環的的時候,則是$data[2]['children'][] = &$value, 而$value指向的是$data[3]
,因此結果是:
$data[ [ "id" => 1, "pid" => 0 , "name" => 'Universe' "children"=>[ // &$data[1], //注意:存儲的是引用 [ "id" => 2, "pid" => 1 , "name" => 'Earth' "children" => [ // &data[2] //注意:存儲的是引用 [ "id" => 3, "pid" => 2 , "name" => 'China' "children" =>[ &$data[3]; //注意:存儲的是引用 ] ] ] ] ] ] ] ... ]
ok,至此,整個執行過程走通了,你懂了嗎?:)
對了,還另一個方法,也是經過引用的,這個我就不分析,要是理解上面的方法,下面的相對來講簡單些。
public static function buildTreeByReference1($data, $id = 'id', $pid = 'pid', $child = "children") { $tmp = []; foreach ($data as $key => $value) { $tmp[$value[$id]] = $value; } $tree = []; foreach ($tmp as $key => $value) { if (isset($tmp[$value['pid']])) { $tmp[$value['pid']]['children'][] = &$tmp[$key]; }else{ $tree[] = &$tmp[$key]; } } return $tree; }