仓酷云

标题: ASP网页设计先容一种效力极高的分类算法 [打印本页]

作者: 萌萌妈妈    时间: 2015-1-16 22:54
标题: ASP网页设计先容一种效力极高的分类算法
优点:简单易学、开发速度快、有很多年“历史”,能找到非常多别人做好的程序来用、配合activeX功能强大,很多php做不到的asp+activeX能做到,例如银行安全控件算法
在网站建立中,分类算法的使用十分的广泛。在计划一个电子商铺时,要触及到商品分类;在计划公布体系时,要触及到栏目大概频道分类;在计划软件下载如许的程序时,要触及到软件的分类;云云等等。能够说,分类是一个很广泛的成绩。
  我经常口试一些程序员,并且我几近毫无破例地要问他们一些关于分类算法的成绩。上面的举几个我经常扣问的成绩。你以为你能够很轻松地回覆么^_^.
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倍以上。
分类编码算法
  成绩就出在后面我们接纳了按次编码,这是一种最复杂的编码办法。人人晓得,复杂其实不意味着效力。实践上,编码迷信是程序员?的课程。上面,我们经由过程计划一种编码算法,使分类的编号ID中同时包括了其父类的信息。一个五级分类的例子以下:
  此例中,用32(4+7+7+7+7)位整数来编码,个中,第一级分类有4位,能够表达16种分类。第二级到第五级分类分离有7位,能够表达128个子分类。
  明显,假如我们失掉一个编码为1092787200的分类,我们就晓得:因为其编码为
01000001001000101001110000000000
  以是它是第四级分类。其父类的二进制编码是01000001001000101000000000000000,十进制编号为1092780032。顺次我们还能够晓得,其父类的父类编码是01000001001000000000000000000000,其父类的父类的父类编码是01000000000000000000000000000000。(我是否是太罗嗦了J,但这一点很主要。再转头看看我们后面提到的第五个成绩。哈哈,这不就已失掉了分类1092787200地点的分类路径了吗?)。
如今我们在一样平常的情形上去会商种别编码成绩。设种别的条理为k,第i层的编码位数为Ni,那末总的编码位数为N(N1+N2+..+Nk)。我们就失掉任何一个种别的编码情势以下:
2^(N-(N1+N2+…+Ni))*j+父类编码
个中,i暗示第i层,j暗示以后层的第j个分类。
如许我们就把任何分类的编码分红了两个部分,个中一部分是它的层编码,一部分是它的父类编码。
由上面公式定一的k个编码我们称为特性码:(由于i能够取k个值,以是有k个)
2^N-2^(N-(N1+N2+…+Ni))
  关于任何给定的种别ID,假如我们把ID和k个特性码"相与",失掉的非0编码,就是其一切父类的编码!
位编码算法
对任何按次编码的Catalog表,我们能够计划一个位编码算法,将一切的种别编码规格化为位编码。在详细完成时,我们先创立一个一时表:
CreateTempCatalog(
[OldID][int]NOTNULL,
[NewID][int]NOTNULL,
[OldFatherID][int]NOTNULL,
[NewFatherID][int]NOTNULL
);
  在这个表中,我们保存一切本来的种别编号OldID和其父类编号OldFatherID,和从头盘算的满意位编码请求的响应编号NewID、NewFatherID。
程序以下:
<%
REMoConn---数据库毗连,已翻开
REMOldFather---本来的父类编号
REMNewFather---新的父类编号
REMN---编码总位数
REMNi--每级的编码位数数组
REMLevel--以后的级数
subFormatAllID(oConn,OldFather,NewFather,N,Nm,Nibyref,Level)
strSQL="selectCatalogID,FatherIDfromCatalogwhereFatherID="&OldFather
setrsCatalog=oConn.Execute(strSQL)
j=1
dowhilenotrsCatalog.EOF
i=2^(N-Nm)*j
ifLeveltheni=i+NewFather

OldCatalog=rsCatalog("CatalogID")
NewCatalog=i
REM写进一时表
strSQL="InsertintoTempCatalog(OldCatalogID,NewCatalogID,OldFatherID,NewFatherID)"
strSQL=strSQL&"values("&OldCatalog&","&NewCatalog&","&OldFather&","&NewFather&")"
Conn.ExecutestrSQL
REM递回挪用FormatAllID
Nm=Nm+Ni(Level+1)
FormatAllIDoConn,OldCatalog,NewCatalog,N,Nm,Ni,Level+1
rsCatalog.MoveNext
j=j+1
loop
rsCatalog.Close
endsub
%>
挪用这个算法的一个例子以下:
<%
REM界说编码参数,个中N为总位数,Ni为每级的位数。
DimN,Ni(5)
Ni(1)=4
N=Ni(1)
fori=2to5
Ni(i)=7
N=N+Ni(i)
next
REM翻开数据库,创立一时表
strSQL="CreateTempCatalog([OldID][int]NOTNULL,[NewID][int]NOTNULL,[OldFatherID][int]NOTNULL,[NewFatherID][int]NOTNULL);"
SetConn=Server.CreateObject("ADODB.Connection")
Conn.OpenApplication("strConn")
Conn.ExecutestrSQL
REM挪用规格化例程
FormatAllIDConn,-1,-1,N,Ni(1),Ni,0
REM------------------------------------------------------------------------
REM在此处更新一切相干表的种别编码为新的编码便可。
REM------------------------------------------------------------------------
REM封闭数据库
strSQL="droptableTempCatalog;"
Conn.ExecutestrSQL
Conn.Close
%>
第四个成绩
  如今我们转头看看第四个成绩:如何失掉某个分类下的一切产物。因为接纳了位编码,如今成绩变得很复杂。我们很简单推算:某个产物属于某个种别的前提是Product.FatherID&(Catalog.ID的特性码)=Catalog.ID。个中"&"代表位与算法。这在SQLServer中是间接撑持的。
  举例来讲:产物所属的种别为:1092787200,而以后种别为1092780032。以后种别对应的特性值为:4294950912,因为1092787200&4294950912=8537400,以是这个产物属于分类8537400。
我们后面已给出了盘算特性码的公式。特性码其实不多,并且很简单盘算,能够思索在Global.asa中Application_OnStart工夫触发时盘算出来,寄存在Application("Mark")数组中。
  固然,有了特性码,我们还能够失掉加倍无效率的算法。我们晓得,固然我们接纳了位编码,实践上仍是一种按次编码的办法。体现出第I级的分类编码一定比第I+1级分类的编码要小。依据这个特性,我们还能够由FID失掉两个特性码,个中一个是本级位特性码FID0,一个是下级位特性码FID1。而产物属于某个分类FID的充实需要前提是:
Product.FatherID>FID0andProduct.FatherID<FID1
上面的程序显现分类FID下的一切产物。因为数据表Product已对FatherID举行索引,故查询速率极快:
<%
REMoConn---数据库毗连,已翻开
REMFID---以后分类
REMFIDMark---特性值数组,典范的情形下为Application("Mark")
REMk---数组元素个数,也是分类的级数
SubGetAllProduct(oConn,FID,FIDMarkbyref,k)
REM依据FID盘算出特性值FID0,FID1
fori=kto1
if(FIDandFIDMark=FID)thenexit
next
strSQL="selectNamefromProductwhereFatherID>"FIDMark(i)&"andFatherID<"FIDMark(i-1)
setrsProduct=oConn.Execute(strSQL)%>
<UL><%
DoWhileNotrsProduct.Eof%>
<LI><%=rsProduct("Name")
Loop%>
</UL><%
rsProduct.Close
EndSub
%>

对于中小型web应用来说,php有很强的竞争力,linux+apache+mysql+php(lamp)的组合几乎可以胜任绝大多数网站的解决方案,对于大型应用来讲,对于系统架构要求更高,需要有成熟的框架支持,jsp的struts是个不错的框架,国内介绍它的资料也非常多,应用逐渐广泛起来。asp就不用说了,
作者: 因胸联盟    时间: 2015-1-19 16:21
下载一个源代码,然后再下载一个VBScript帮助,在源代码中遇到不认识的函数或是其他什么程序,都可以查帮助进行解决,这样学习效率很高。
作者: 透明    时间: 2015-1-24 13:56
多看多学多思。多看一些关于ASP的书籍,一方面可以扩展知识面一方面可以鉴借别人是如何掌握、运用ASP的;多学善于关注别人,向同学老师多多学习,不论知识的大小;多思则是要将学到的知识灵活运用。
作者: 仓酷云    时间: 2015-2-1 16:23
哪些内置对象是可以跳过的,或者哪些属性和方法是用不到的?
作者: 海妖    时间: 2015-2-7 08:38
没有坚实的理论做基础,那么我们连踏入社会第一步的资本都没有,特别对于计算机专业的学生学好专业知识是置关重要的。在这里我侧重讲一下如何学习ASP,从平时的学习过程中。
作者: 若相依    时间: 2015-2-21 06:41
先学习用frontpage熟悉html编辑然后学习asp和vbscript建议买书进行系统学习
作者: 爱飞    时间: 2015-3-6 19:59
没有坚实的理论做基础,那么我们连踏入社会第一步的资本都没有,特别对于计算机专业的学生学好专业知识是置关重要的。在这里我侧重讲一下如何学习ASP,从平时的学习过程中。
作者: 小女巫    时间: 2015-3-13 07:09
运用ASP可将VBscript、javascript等脚本语言嵌入到HTML中,便可快速完成网站的应用程序,无需编译,可在服务器端直接执行。容易编写,使用普通的文本编辑器编写,如记事本就可以完成。由脚本在服务器上而不是客户端运行,ASP所使用的脚本语言都在服务端上运行。
作者: 萌萌妈妈    时间: 2015-3-20 16:25
多看多学多思。多看一些关于ASP的书籍,一方面可以扩展知识面一方面可以鉴借别人是如何掌握、运用ASP的;多学善于关注别人,向同学老师多多学习,不论知识的大小;多思则是要将学到的知识灵活运用。




欢迎光临 仓酷云 (http://ckuyun.com/) Powered by Discuz! X3.2