幾篇關(guān)于無(wú)限分類算法的文章第2/5頁(yè)
http://www.nirvanastudio.org/php/hierarchical-data-database.html
中文翻譯
在數(shù)據(jù)庫(kù)中存儲(chǔ)層次數(shù)據(jù)
作者:Gijs Van Tulder
翻譯:ShiningRay @ NirvanaStudio
無(wú)論你要構(gòu)建自己的論壇,在你的網(wǎng)站上發(fā)布消息還是書寫自己的cms [1]程序,你都會(huì)遇到要在數(shù)據(jù)庫(kù)中存儲(chǔ)層次數(shù)據(jù)的情況。同時(shí),除非你使用一種像XML [2]的數(shù)據(jù)庫(kù),否則關(guān)系數(shù)據(jù)庫(kù)中的表都不是層次結(jié)構(gòu)的,他們只是一個(gè)平坦的列表。所以你必須找到一種把層次數(shù)據(jù)庫(kù)轉(zhuǎn)化的方法。
存儲(chǔ)樹形結(jié)構(gòu)是一個(gè)很常見(jiàn)的問(wèn)題,他有好幾種解決方案。主要有兩種方法:鄰接列表模型和改進(jìn)前序遍歷樹算法
在本文中,我們將探討這兩種保存層次數(shù)據(jù)的方法。我將舉一個(gè)在線食品店樹形圖的例子。這個(gè)食品店通過(guò)類別、顏色和品種來(lái)組織食品。樹形圖如下:
本文包含了一些代碼的例子來(lái)演示如何保存和獲取數(shù)據(jù)。我選擇PHP [3]來(lái)寫例子,因?yàn)槲页S眠@個(gè)語(yǔ)言,而且很多人也都使用或者知道這個(gè)語(yǔ)言。你可以很方便地把它們翻譯成你自己用的語(yǔ)言。
鄰接列表模型(The Adjacency List Model)
我們要嘗試的第一個(gè)——也是最優(yōu)美的——方法稱為“鄰接列表模型”或稱為“遞歸方法”。它是一個(gè)很優(yōu)雅的方法因?yàn)槟阒恍枰粋€(gè)簡(jiǎn)單的方法來(lái)在你的樹中進(jìn)行迭代。在我們的食品店中,鄰接列表的表格如下:
如你所見(jiàn),對(duì)每個(gè)節(jié)點(diǎn)保存一個(gè)“父”節(jié)點(diǎn)。我們可以看到“Pear [4]”是“Green”的一個(gè)子節(jié)點(diǎn),而后者又是“Fruit”的子節(jié)點(diǎn),如此類推。根節(jié)點(diǎn),“Food”,則他的父節(jié)點(diǎn)沒(méi)有值。為了簡(jiǎn)單,我只用了“title”值來(lái)標(biāo)識(shí)每個(gè)節(jié)點(diǎn)。當(dāng)然,在實(shí)際的數(shù)據(jù)庫(kù)中,你要使用數(shù)字的ID。
顯示樹
現(xiàn)在我們已經(jīng)把樹放入數(shù)據(jù)庫(kù)中了,得寫一個(gè)顯示函數(shù)了。這個(gè)函數(shù)將從根節(jié)點(diǎn)開(kāi)始——沒(méi)有父節(jié)點(diǎn)的節(jié)點(diǎn)——同時(shí)要顯示這個(gè)節(jié)點(diǎn)所有的子節(jié)點(diǎn)。對(duì)于這些子節(jié)點(diǎn),函數(shù)也要獲取并顯示這個(gè)子節(jié)點(diǎn)的子節(jié)點(diǎn)。然后,對(duì)于他們的子節(jié)點(diǎn),函數(shù)還要再顯示所有的子節(jié)點(diǎn),然后依次類推。
也許你已經(jīng)注意到了,這種函數(shù)的描述,有一種普遍的模式。我們可以簡(jiǎn)單地只寫一個(gè)函數(shù),用來(lái)獲得特定節(jié)點(diǎn)的子節(jié)點(diǎn)。這個(gè)函數(shù)然后要對(duì)每個(gè)子節(jié)點(diǎn)調(diào)用自身來(lái)再次顯示他們的子節(jié)點(diǎn)。這就是“遞歸”機(jī)制,因此稱這種方法叫“遞歸方法”。
<?php
// $parent 是我們要查看的子節(jié)點(diǎn)的父節(jié)點(diǎn)
// $level 會(huì)隨著我們深入樹的結(jié)構(gòu)而不斷增加,
// 用來(lái)顯示一個(gè)清晰的縮進(jìn)格式
function display_children($parent, $level) {
// 獲取$parent的全部子節(jié)點(diǎn)
$result = mysql_query('SELECT title FROM tree '.
'WHERE parent="'.$parent.'";');
// 顯示每個(gè)節(jié)點(diǎn)
while ($row = mysql_fetch_array($result)) {
// 縮進(jìn)并顯示他的子節(jié)點(diǎn)的標(biāo)題
echo str_repeat(' ',$level).$row['title']."\n";
// 再次調(diào)用這個(gè)函數(shù)來(lái)顯著這個(gè)子節(jié)點(diǎn)的子節(jié)點(diǎn)
display_children($row['title'], $level+1);
}
}
?>
要實(shí)現(xiàn)整個(gè)樹,我們只要調(diào)用函數(shù)時(shí)用一個(gè)空字符串作為$parent
和$level = 0: display_children('',0);
函數(shù)返回了我們的食品店的樹狀圖如下:
Food
Fruit
Red
Cherry
Yellow
Banana
Meat
Beef
Pork
注意如果你只想看一個(gè)子樹,你可以告訴函數(shù)從另一個(gè)節(jié)點(diǎn)開(kāi)始。例如,要顯示“Fruit”子樹,你只要display_children('Fruit',0);
The Path to a Node節(jié)點(diǎn)的路徑
利用差不多的函數(shù),我們也可以查詢某個(gè)節(jié)點(diǎn)的路徑如果你只知道這個(gè)節(jié)點(diǎn)的名字或者ID。例如,“Cherry”的路徑是“Food”>“Fruit”>“Red”。要獲得這個(gè)路徑,我們的函數(shù)要獲得這個(gè)路徑,這個(gè)函數(shù)必須從最深的層次開(kāi)始:“Cheery”。但后查找這個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn),并添加到路徑中。在我們的例子中,這個(gè)父節(jié)點(diǎn)是“Red”。如果我們知道“Red”是“Cherry”的父節(jié)點(diǎn)。
<?php
// $node 是我們要查找路徑的那個(gè)節(jié)點(diǎn)的名字
function get_path($node) {
// 查找這個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)
$result = mysql_query('SELECT parent FROM tree '.
'WHERE title="'.$node.'";');
$row = mysql_fetch_array($result);
// 在這個(gè)array [5] 中保存數(shù)組
$path = array();
// 如果 $node 不是根節(jié)點(diǎn),那么繼續(xù)
if ($row['parent']!='') {
// $node 的路徑的最后一部分是$node父節(jié)點(diǎn)的名稱
$path[] = $row['parent'];
// 我們要添加這個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)的路徑到現(xiàn)在這個(gè)路徑
$path = array_merge(get_path($row['parent']), $path);
}
// 返回路徑
return $path;
}
?>
這個(gè)函數(shù)現(xiàn)在返回了指定節(jié)點(diǎn)的路徑。他把路徑作為數(shù)組返回,這樣我們可以使用print_r(get_path('Cherry'));
來(lái)顯示,其結(jié)果是:
Array
(
[0] => Food
[1] => Fruit
[2] => Red
)
不足
正如我們所見(jiàn),這確實(shí)是一個(gè)很好的方法。他很容易理解,同時(shí)代碼也很簡(jiǎn)單。但是鄰接列表模型的缺點(diǎn)在哪里呢?在大多數(shù)編程語(yǔ)言中,他運(yùn)行很慢,效率很差。這主要是“遞歸”造成的。我們每次查詢節(jié)點(diǎn)都要訪問(wèn)數(shù)據(jù)庫(kù)。
每次數(shù)據(jù)庫(kù)查詢都要花費(fèi)一些時(shí)間,這讓函數(shù)處理龐大的樹時(shí)會(huì)十分慢。
造成這個(gè)函數(shù)不是太快的第二個(gè)原因可能是你使用的語(yǔ)言。不像Lisp這類語(yǔ)言,大多數(shù)語(yǔ)言不是針對(duì)遞歸函數(shù)設(shè)計(jì)的。對(duì)于每個(gè)節(jié)點(diǎn),函數(shù)都要調(diào)用他自己,產(chǎn)生新的實(shí)例。這樣,對(duì)于一個(gè)4層的樹,你可能同時(shí)要運(yùn)行4個(gè)函數(shù)副本。對(duì)于每個(gè)函數(shù)都要占用一塊內(nèi)存并且需要一定的時(shí)間初始化,這樣處理大樹時(shí)遞歸就很慢了。
改進(jìn)前序遍歷樹
現(xiàn)在,讓我們看另一種存儲(chǔ)樹的方法。遞歸可能會(huì)很慢,所以我們就盡量不使用遞歸函數(shù)。我們也想盡量減少數(shù)據(jù)庫(kù)查詢的次數(shù)。最好是每次只需要查詢一次。
我們先把樹按照水平方式擺開(kāi)。從根節(jié)點(diǎn)開(kāi)始(“Food”),然后他的左邊寫上1。然后按照樹的順序(從上到下)給“Fruit”的左邊寫上2。這樣,你沿著樹的邊界走啊走(這就是“遍歷”),然后同時(shí)在每個(gè)節(jié)點(diǎn)的左邊和右邊寫上數(shù)字。最后,我們回到了根節(jié)點(diǎn)“Food”在右邊寫上18。下面是標(biāo)上了數(shù)字的樹,同時(shí)把遍歷的順序用箭頭標(biāo)出來(lái)了。
我們稱這些數(shù)字為左值和右值(如,“Food”的左值是1,右值是18)。正如你所見(jiàn),這些數(shù)字按時(shí)了每個(gè)節(jié)點(diǎn)之間的關(guān)系。因?yàn)椤癛ed”有3和6兩個(gè)值,所以,它是有擁有1-18值的“Food”節(jié)點(diǎn)的后續(xù)。同樣的,我們可以推斷所有左值大于2并且右值小于11的節(jié)點(diǎn),都是有2-11的“Food”節(jié)點(diǎn)的后續(xù)。這樣,樹的結(jié)構(gòu)就通過(guò)左值和右值儲(chǔ)存下來(lái)了。這種數(shù)遍整棵樹算節(jié)點(diǎn)的方法叫做“改進(jìn)前序遍歷樹”算法。
在繼續(xù)前,我們先看看我們的表格里的這些值:
注意單詞“l(fā)eft”和“right”在SQL中有特殊的含義。因此,我們只能用“l(fā)ft”和“rgt”來(lái)表示這兩個(gè)列。(譯注——其實(shí)Mysql中可以用“`”來(lái)表示,如“`left`”,MSSQL中可以用“[]”括出,如“[left]”,這樣就不會(huì)和關(guān)鍵詞沖突了。)同樣注意這里我們已經(jīng)不需要“parent”列了。我們只需要使用lft和rgt就可以存儲(chǔ)樹的結(jié)構(gòu)。
獲取樹
如果你要通過(guò)左值和右值來(lái)顯示這個(gè)樹的話,你要首先標(biāo)識(shí)出你要獲取的那些節(jié)點(diǎn)。例如,如果你想獲得“Fruit”子樹,你要選擇那些左值在2到11的節(jié)點(diǎn)。用SQL語(yǔ)句表達(dá):
SELECT * FROM tree WHERE lft BETWEEN 2 AND 11;
這個(gè)會(huì)返回:
好吧,現(xiàn)在整個(gè)樹都在一個(gè)查詢中了?,F(xiàn)在就要像前面的遞歸函數(shù)那樣顯示這個(gè)樹,我們要加入一個(gè)ORDER BY子句在這個(gè)查詢中。如果你從表中添加和刪除行,你的表可能就順序不對(duì)了,我們因此需要按照他們的左值來(lái)進(jìn)行排序。
SELECT * FROM tree WHERE lft BETWEEN 2 AND 11 ORDER BY lft ASC;
就只剩下縮進(jìn)的問(wèn)題了。
要顯示樹狀結(jié)構(gòu),子節(jié)點(diǎn)應(yīng)該比他們的父節(jié)點(diǎn)稍微縮進(jìn)一些。我們可以通過(guò)保存一個(gè)右值的一個(gè)棧。每次你從一個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn)開(kāi)始時(shí),你把這個(gè)節(jié)點(diǎn)的右值添加到棧中。你也知道子節(jié)點(diǎn)的右值都比父節(jié)點(diǎn)的右值小,這樣通過(guò)比較當(dāng)前節(jié)點(diǎn)和棧中的前一個(gè)節(jié)點(diǎn)的右值,你可以判斷你是不是在顯示這個(gè)父節(jié)點(diǎn)的子節(jié)點(diǎn)。當(dāng)你顯示完這個(gè)節(jié)點(diǎn),你就要把他的右值從棧中刪除。要獲得當(dāng)前節(jié)點(diǎn)的層數(shù),只要數(shù)一下棧中的元素。
<?php
function display_tree($root) {
// 獲得$root節(jié)點(diǎn)的左邊和右邊的值
$result = mysql_query('SELECT lft, rgt FROM tree '.
'WHERE title="'.$root.'";');
$row = mysql_fetch_array($result);
// 以一個(gè)空的$right棧開(kāi)始
$right = array();
// 現(xiàn)在,獲得$root節(jié)點(diǎn)的所有后序
$result = mysql_query('SELECT title, lft, rgt FROM tree '.
'WHERE lft BETWEEN '.$row['lft'].' AND '.
$row['rgt'].' ORDER BY lft ASC;');
// 顯示每一行
while ($row = mysql_fetch_array($result)) {
// 檢查棧里面有沒(méi)有元素
if (count($right)>0) {
// 檢查我們是否需要從棧中刪除一個(gè)節(jié)點(diǎn)
while ($right[count($right)-1]<$row['rgt']) {
array_pop($right);
}
}
// 顯示縮進(jìn)的節(jié)點(diǎn)標(biāo)題
echo str_repeat(' ',count($right)).$row['title']."\n";
// 把這個(gè)節(jié)點(diǎn)添加到棧中
$right[] = $row['rgt'];
}
}
?>
如果運(yùn)行這段代碼,你可以獲得和上一部分討論的遞歸函數(shù)一樣的結(jié)果。而這個(gè)函數(shù)可能會(huì)更快一點(diǎn):他不采用遞歸而且只是用了兩個(gè)查詢
節(jié)點(diǎn)的路徑
有了新的算法,我們還要另找一種新的方法來(lái)獲得指定節(jié)點(diǎn)的路徑。這樣,我們就需要這個(gè)節(jié)點(diǎn)的祖先的一個(gè)列表。
由于新的表結(jié)構(gòu),這不需要花太多功夫。你可以看一下,例如,4-5的“Cherry”節(jié)點(diǎn),你會(huì)發(fā)現(xiàn)祖先的左值都小于4,同時(shí)右值都大于5。這樣,我們就可以使用下面這個(gè)查詢:
SELECT title FROM tree WHERE lft < 4 AND rgt > 5 ORDER BY lft ASC;
注意,就像前面的查詢一樣,我們必須使用一個(gè)ORDER BY子句來(lái)對(duì)節(jié)點(diǎn)排序。這個(gè)查詢將返回:
+-------+
| title |
+-------+
| Food |
| Fruit |
| Red |
+-------+
我們現(xiàn)在只要把各行連起來(lái),就可以得到“Cherry”的路徑了。
有多少個(gè)后續(xù)節(jié)點(diǎn)?How Many Descendants
如果你給我一個(gè)節(jié)點(diǎn)的左值和右值,我就可以告訴你他有多少個(gè)后續(xù)節(jié)點(diǎn),只要利用一點(diǎn)點(diǎn)數(shù)學(xué)知識(shí)。
因?yàn)槊總€(gè)后續(xù)節(jié)點(diǎn)依次會(huì)對(duì)這個(gè)節(jié)點(diǎn)的右值增加2,所以后續(xù)節(jié)點(diǎn)的數(shù)量可以這樣計(jì)算:
descendants = (right – left - 1) / 2
利用這個(gè)簡(jiǎn)單的公式,我可以立刻告訴你2-11的“Fruit”節(jié)點(diǎn)有4個(gè)后續(xù)節(jié)點(diǎn),8-9的“Banana”節(jié)點(diǎn)只是1個(gè)子節(jié)點(diǎn),而不是父節(jié)點(diǎn)。
自動(dòng)化樹遍歷
現(xiàn)在你對(duì)這個(gè)表做一些事情,我們應(yīng)該學(xué)習(xí)如何自動(dòng)的建立表了。這是一個(gè)不錯(cuò)的練習(xí),首先用一個(gè)小的樹,我們也需要一個(gè)腳本來(lái)幫我們完成對(duì)節(jié)點(diǎn)的計(jì)數(shù)。
讓我們先寫一個(gè)腳本用來(lái)把一個(gè)鄰接列表轉(zhuǎn)換成前序遍歷樹表格。
<?php
function rebuild_tree($parent, $left) {
// 這個(gè)節(jié)點(diǎn)的右值是左值加1
$right = $left+1;
// 獲得這個(gè)節(jié)點(diǎn)的所有子節(jié)點(diǎn)
$result = mysql_query('SELECT title FROM tree '.
'WHERE parent="'.$parent.'";');
while ($row = mysql_fetch_array($result)) {
// 對(duì)當(dāng)前節(jié)點(diǎn)的每個(gè)子節(jié)點(diǎn)遞歸執(zhí)行這個(gè)函數(shù)
// $right 是當(dāng)前的右值,它會(huì)被rebuild_tree函數(shù)增加
$right = rebuild_tree($row['title'], $right);
}
// 我們得到了左值,同時(shí)現(xiàn)在我們已經(jīng)處理這個(gè)節(jié)點(diǎn)我們知道右值的子節(jié)點(diǎn)
mysql_query('UPDATE tree SET lft='.$left.', rgt='.
$right.' WHERE title="'.$parent.'";');
// 返回該節(jié)點(diǎn)的右值+1
return $right+1;
}
?>
這是一個(gè)遞歸函數(shù)。你要從rebuild_tree('Food',1);
開(kāi)始,這個(gè)函數(shù)就會(huì)獲取所有的“Food”節(jié)點(diǎn)的子節(jié)點(diǎn)。
如果沒(méi)有子節(jié)點(diǎn),他就直接設(shè)置它的左值和右值。左值已經(jīng)給出了,1,右值則是左值加1。如果有子節(jié)點(diǎn),函數(shù)重復(fù)并且返回最后一個(gè)右值。這個(gè)右值用來(lái)作為“Food”的右值。
遞歸讓這個(gè)函數(shù)有點(diǎn)復(fù)雜難于理解。然而,這個(gè)函數(shù)確實(shí)得到了同樣的結(jié)果。他沿著樹走,添加每一個(gè)他看見(jiàn)的節(jié)點(diǎn)。你運(yùn)行了這個(gè)函數(shù)之后,你會(huì)發(fā)現(xiàn)左值和右值和預(yù)期的是一樣的(一個(gè)快速檢驗(yàn)的方法:根節(jié)點(diǎn)的右值應(yīng)該是節(jié)點(diǎn)數(shù)量的兩倍)。
添加一個(gè)節(jié)點(diǎn)
我們?nèi)绾谓o這棵樹添加一個(gè)節(jié)點(diǎn)?有兩種方式:在表中保留“parent”列并且重新運(yùn)行rebuild_tree()
函數(shù)——一個(gè)很簡(jiǎn)單但卻不是很優(yōu)雅的函數(shù);或者你可以更新所有新節(jié)點(diǎn)右邊的節(jié)點(diǎn)的左值和右值。
第一個(gè)想法比較簡(jiǎn)單。你使用鄰接列表方法來(lái)更新,同時(shí)使用改進(jìn)前序遍歷樹來(lái)查詢。如果你想添加一個(gè)新的節(jié)點(diǎn),你只需要把節(jié)點(diǎn)插入表格,并且設(shè)置好parent列。然后,你只需要重新運(yùn)行rebuild_tree()
函數(shù)。這做起來(lái)很簡(jiǎn)單,但是對(duì)大的樹效率不高。
第二種添加和刪除節(jié)點(diǎn)的方法是更新新節(jié)點(diǎn)右邊的所有節(jié)點(diǎn)。讓我們看一下例子。我們要添加一種新的水果——“Strawberry”,作為“Red”的最后一個(gè)子節(jié)點(diǎn)。首先,我們要騰出一個(gè)空間?!癛ed”的右值要從6變成8,7-10的“Yellow”節(jié)點(diǎn)要變成9-12,如此類推。更新“Red”節(jié)點(diǎn)意味著我們要把所有左值和右值大于5的節(jié)點(diǎn)加上2。
我們用一下查詢:
UPDATE tree SET rgt=rgt+2 WHERE rgt>5;
UPDATE tree SET lft=lft+2 WHERE lft>5;
現(xiàn)在我們可以添加一個(gè)新的節(jié)點(diǎn)“Strawberry”來(lái)填補(bǔ)這個(gè)新的空間。這個(gè)節(jié)點(diǎn)左值為6右值為7。
INSERT INTO tree SET lft=6, rgt=7, title='Strawberry';
如果我們運(yùn)行display_tree()
函數(shù),我們將發(fā)現(xiàn)我們新的“Strawberry”節(jié)點(diǎn)已經(jīng)成功地插入了樹中:
Food
Fruit
Red
Cherry
Strawberry
Yellow
Banana
Meat
Beef
Pork
缺點(diǎn)
首先,改進(jìn)前序遍歷樹算法看上去很難理解。它當(dāng)然沒(méi)有鄰接列表方法簡(jiǎn)單。然而,一旦你習(xí)慣了左值和右值這兩個(gè)屬性,他就會(huì)變得清晰起來(lái),你可以用這個(gè)技術(shù)來(lái)完成臨街列表能完成的所有事情,同時(shí)改進(jìn)前序遍歷樹算法更快。當(dāng)然,更新樹需要很多查詢,要慢一點(diǎn),但是取得節(jié)點(diǎn)卻可以只用一個(gè)查詢。
總結(jié)
你現(xiàn)在已經(jīng)對(duì)兩種在數(shù)據(jù)庫(kù)存儲(chǔ)樹方式熟悉了吧。雖然在我這兒改進(jìn)前序遍歷樹算法性能更好,但是也許在你特殊的情況下鄰接列表方法可能表現(xiàn)更好一些。這個(gè)就留給你自己決定了
最后一點(diǎn):就像我已經(jīng)說(shuō)得我部推薦你使用節(jié)點(diǎn)的標(biāo)題來(lái)引用這個(gè)節(jié)點(diǎn)。你應(yīng)該遵循數(shù)據(jù)庫(kù)標(biāo)準(zhǔn)化的基本規(guī)則。我沒(méi)有使用數(shù)字標(biāo)識(shí)是因?yàn)橛昧酥罄泳捅容^難讀。
進(jìn)一步閱讀
數(shù)據(jù)庫(kù)指導(dǎo) Joe Celko寫的更多關(guān)于SQL數(shù)據(jù)庫(kù)中的樹的問(wèn)題:
http://searchdatabase.techtarget.com/tip/1,289483,sid13_gci537290,00.html [6]
另外兩種處理層次數(shù)據(jù)的方法:
http://www.evolt.org/article/Four_ways_to_work_with_hierarchical_data/17/4047/index.html [7]
Xindice, “本地XML數(shù)據(jù)庫(kù)”:
http://xml.apache.org/xindice/ [8]
遞歸的一個(gè)解釋:
http://www.strath.ac.uk/IT/Docs/Ccourse/subsection3_9_5.html [9]
[1] /glossary.php?q=C#term_28
[2] /glossary.php?q=X#term_3
[3] /glossary.php?q=P#term_1
[4] /glossary.php?q=P#term_50
[5] /glossary.php?q=%23#term_72
[6] http://searchdatabase.techtarget.com/tip/1,289483,sid13_gci537290,00.html
[7] http://www.evolt.org/article/Four_ways_to_work_with_hierarchical_data/17/4047/index.html
[8] http://xml.apache.org/xindice/
[9] http://www.strath.ac.uk/IT/Docs/Ccourse/subsection3_9_5.html
- 解析左右值無(wú)限分類的實(shí)現(xiàn)算法
- 解析thinkphp的左右值無(wú)限分類
- php無(wú)限分類且支持輸出樹狀圖的詳細(xì)介紹
- 利用php遞歸實(shí)現(xiàn)無(wú)限分類 格式化數(shù)組的詳解
- PHP無(wú)限分類(樹形類)的深入分析
- 基于php無(wú)限分類的深入理解
- 比較簡(jiǎn)單實(shí)用的PHP無(wú)限分類源碼分享(思路不錯(cuò))
- PHP 無(wú)限分類三種方式 非函數(shù)的遞歸調(diào)用!
- PHP無(wú)限分類代碼,支持?jǐn)?shù)組格式化、直接輸出菜單兩種方式
- 一個(gè)很簡(jiǎn)單的無(wú)限分類樹實(shí)現(xiàn)代碼
- php遞歸實(shí)現(xiàn)無(wú)限分類生成下拉列表的函數(shù)
- php用數(shù)組返回?zé)o限分類的列表數(shù)據(jù)的代碼
- 刪除無(wú)限分類并同時(shí)刪除它下面的所有子分類的方法
- php 無(wú)限分類的樹類代碼
- asp.net 無(wú)限分類
- 自己前幾天寫的無(wú)限分類類
- 帖幾個(gè)PHP的無(wú)限分類實(shí)現(xiàn)想法~
- PHP 循環(huán)刪除無(wú)限分類子節(jié)點(diǎn)的實(shí)現(xiàn)代碼
相關(guān)文章
php設(shè)計(jì)模式 Chain Of Responsibility (職責(zé)鏈模式)
為解除請(qǐng)求的發(fā)送者和接收者之間的耦合,而使用多個(gè)對(duì)象都用機(jī)會(huì)處理這個(gè)請(qǐng)求,將這些對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有一個(gè)對(duì)象處理它2011-06-06PHP計(jì)算指定日期所在周的開(kāi)始和結(jié)束日期的方法
這篇文章主要介紹了PHP計(jì)算指定日期所在周的開(kāi)始和結(jié)束日期的方法,涉及php操作日期時(shí)間的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03圖文詳解phpstorm配置Xdebug進(jìn)行調(diào)試PHP教程
這篇文章主要為大家詳細(xì)的介紹了phpstorm配置Xdebug進(jìn)行調(diào)試PHP教程 ,感興趣的小伙伴們可以參考一下2016-06-06PHP使用PDO創(chuàng)建MySQL數(shù)據(jù)庫(kù)、表及插入多條數(shù)據(jù)操作示例
這篇文章主要介紹了PHP使用PDO創(chuàng)建MySQL數(shù)據(jù)庫(kù)、表及插入多條數(shù)據(jù)操作,結(jié)合實(shí)例形式總結(jié)分析了php基于pdo的mysql數(shù)據(jù)庫(kù)創(chuàng)建、數(shù)據(jù)表創(chuàng)建以及多條數(shù)據(jù)插入操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-05-05PHP實(shí)現(xiàn)鏈?zhǔn)讲僮鞯娜N方法詳解
這篇文章主要介紹了PHP實(shí)現(xiàn)鏈?zhǔn)讲僮鞯娜N方法,結(jié)合實(shí)例形式分析了php鏈?zhǔn)讲僮鞯南嚓P(guān)實(shí)現(xiàn)技巧與使用注意事項(xiàng),需要的朋友可以參考下2017-11-11PHP中構(gòu)造函數(shù)和析構(gòu)函數(shù)解析
這篇文章主要介紹了PHP中構(gòu)造函數(shù)和析構(gòu)函數(shù)解析,本文用代碼實(shí)例講解了PHP中構(gòu)造函數(shù)和析構(gòu)函數(shù),需要的朋友可以參考下2014-10-10php采集中國(guó)代理服務(wù)器網(wǎng)的方法
這篇文章主要介紹了php采集中國(guó)代理服務(wù)器網(wǎng)的方法,涉及php采集的相關(guān)使用技巧,需要的朋友可以參考下2015-06-06