分類演算法要解決的問題

來源:互聯網
上載者:User
解決|演算法|問題 在網站建設中,分類演算法的應用非常的普遍。在設計一個電子商店時,要涉及到商品分類;在設計發布系統時,要涉及到欄目或者頻道分類;在設計軟體下載這樣的程式時,要涉及到軟體的分類;如此等等。可以說,分類是一個很普遍的問題。

我常常面試一些程式員,而且我幾乎毫無例外地要問他們一些關於分類演算法的問題。下面的舉幾個我常常詢問的問題。你認為你可以很輕鬆地回答麼^_^.

1、分類演算法常常表現為樹的表示和遍曆問題。那麼,請問:如果用資料庫中的一個Table來表達樹型分類,應該有幾個欄位?
2、如何快速地從這個Table恢複出一棵樹;
3、如何判斷某個分類是否是另一個分類的子類;
4、如何尋找某個分類的所有產品;
5、如何產生分類所在的路徑。
6、如何新增分類;

在不限制分類的級數和每級分類的個數時,這些問題並不是可以輕鬆回答的。本文試圖解決這些問題。

分類的資料結構

我們知道:分類的資料結構實際上是一棵樹。在《資料結構》課程中,大家可能學過Tree的演算法。由於在網站建設中我們大量使用資料庫,所以我們將從Tree在資料庫中的儲存談起。

為簡化問題,我們假設每個節點只需要保留Name這一個資訊。我們需要為每個節點編號。編號的方法有很多種。在資料庫中常用的就是自動編號。這在Access、SQLServer、Oracle中都是這樣。假設編號欄位為ID。為了表示某個節點ID1是另外一個節點ID2的父節點,我們需要在資料庫中再保留一個欄位,說明這個分類是屬於哪個節點的兒子。把這個欄位取名為FatherID。如這裡的ID2,其FatherID就是ID1。這樣,我們就得到了分類Catalog的資料表定義:

CreateTable[Catalog](
[ID][int]NOTNULL,
[Name][nvarchar](50)NOTNULL,
[FatherID][int]NOTNULL
);

約定:我們約定用-1作為最上面一層分類的父親編碼。編號為-1的分類。這是一個虛擬分類。它在資料庫中沒有記錄。

如何恢複出一棵樹

上面的Catalog定義的最大優勢,就在於用它可以輕鬆地恢複出一棵樹—分類樹。為了更清楚地展示演算法,我們先考慮一個簡單的問題:怎樣顯示某個分類的下一級分類。我們知道,要查詢某個分類FID的下一級分類,SQL語句非常簡單:
selectNamefromcatalogwhereFatherID=FID
顯示這些類別時,我們簡單地用<LI>來做到:
<%
REMoConn---資料庫連接,調用GetChildren時已經開啟
REMFID-----當前分類的編號

FunctionGetChildren(oConn,FID)
strSQL="selectID,NamefromcatalogwhereFatherID="&FID
setrsCatalog=oConn.Execute(strSQL)
%>
<UL>
<%
DowhilenotrsCatalog.Eof
%>
<LI><%=rsCatalog("Name")%>
<%
Loop
%>
</UL>
<%
rsCatalog.Close
EndFunction
%>

現在我們來看看如何顯示FID下的所有分類。這需要用到遞迴演算法。我們只需要在GetChildren函數中簡單地對所有ID進行調用:GetChildren(oConn,Catalog(“ID”))就可以了。
<%
REMoConn---資料庫連接,已經開啟
REMFID-----當前分類的編號

FunctionGetChildren(oConn,FID)
strSQL="selectNamefromcatalogwhereFatherID="&FID
setrsCatalog=oConn.Execute(strSQL)
%>
<UL>
<%
DowhilenotrsCatalog.Eof
%>
<LI><%=rsCatalog("Name")%><%=GetChildren(oConn,Catalog("ID"))%>
<%
Loop
%>
</UL>
<%
rsCatalog.Close
EndFunction
%>

修改後的GetChildren就可以完成顯示FID分類的所有子分類的任務。要顯示所有的分類,只需要如此調用就可以了:
<%
REMstrConn--串連資料庫的字串,請根據情況修改

setoConn=Server.CreateObject("ADODB.Connection")
oConn.OpenstrConn
GetChildren(oConn,-1)
oConn.Close
%>

如何尋找某個分類的所有產品;
現在來解決我們在前面提出的第四個問題。第三個問題留作習題。我們假設產品的資料表如下定義:

CreateTableProduct(
[ID][int]NOTNULL,
[Name][nvchar]NOTNULL,
[FatherID][int]NOTNULL
);

其中,ID是產品的編號,Name是產品的名稱,而FatherID是產品所屬的分類。

對第四個問題,很容易想到的辦法是:先找到這個分類FID的所有子類,然後查詢所有子類下的所有產品。實現這個演算法實際上很複雜。代碼大致如下:

<%
FunctionGetAllID(oConn,FID)
DimstrTemp

IfFID=-1then
strTemp=""
else
strTemp=","
endif

strSQL="selectNamefromcatalogwhereFatherID="&FID
setrsCatalog=oConn.Execute(strSQL)
DowhilenotrsCatalog.Eof
strTemp=strTemp&rsCatalog("ID")&GetAllID(oConn,Catalog("ID"))REM遞迴調用
Loop
rsCatalog.Close

GetAllID=strTemp

EndFunction

REMstrConn--串連資料庫的字串,請根據情況修改

setoConn=Server.CreateObject("ADODB.Connection")
oConn.OpenstrConn

FID=Request.QueryString("FID")

strSQL="selecttop100*fromProductwhereFatherIDin("&GetAllID(oConn,FID)&")"
setrsProduct=oConn.Execute(strSQL)
%>

<UL><%
DowhilenotrsProduct.EOF
%>
<LI><%=rsProduct("Name")%>
<%
Loop
%>
</UL>

<%rsProduct.Close
oConn.Close
%>

這個演算法有很多缺點。試列舉幾個如下:

1、由於我們需要查詢FID下的所有分類,當分類非常多時,演算法將非常地不經濟,而且,由於要構造一個很大的strSQL,試想如果有1000個分類,這個strSQL將很大,能否執行就是一個問題。

2、我們知道,在SQL中使用In子句的效率是非常低的。這個演算法不可避免地要使用In子句,效率很低。

我發現80%以上的程式員鐘愛這樣的演算法,並在很多系統中大量地使用。細心的程式員會發現他們寫出了很慢的程式,但苦於找不到原因。他們反覆地檢查SQL的執行效率,提高機器的檔次,但效率的增加很少。

最根本的問題就出在這個演算法本身。演算法定了,能夠再最佳化的機會就不多了。我們下面來介紹一種演算法,效率將是上面演算法的10倍以上。

相關文章

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。