asp实现无限级分类的问题.docx

上传人:b****3 文档编号:2851911 上传时间:2022-11-15 格式:DOCX 页数:9 大小:19.21KB
下载 相关 举报
asp实现无限级分类的问题.docx_第1页
第1页 / 共9页
asp实现无限级分类的问题.docx_第2页
第2页 / 共9页
asp实现无限级分类的问题.docx_第3页
第3页 / 共9页
asp实现无限级分类的问题.docx_第4页
第4页 / 共9页
asp实现无限级分类的问题.docx_第5页
第5页 / 共9页
点击查看更多>>
下载资源
资源描述

asp实现无限级分类的问题.docx

《asp实现无限级分类的问题.docx》由会员分享,可在线阅读,更多相关《asp实现无限级分类的问题.docx(9页珍藏版)》请在冰豆网上搜索。

asp实现无限级分类的问题.docx

asp实现无限级分类的问题

asp实现无限级分类的问题

分类算法要解决的问题

在网站建设中,分类算法的应用非常的普遍。

在设计一个电子商店时,要涉及到商品分类;在设计发布系统时,要涉及到栏目或者频道分类;在设计软件下载这样的程序时,要涉及到软件的分类;如此等等。

可以说,分类是一个很普遍的问题。

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

显示这些类别时,我们简单地用

来做到:

<%

REMoConn---数据库连接,调用GetChildren时已经打开

REMFID-----当前分类的编号

FunctionGetChildren(oConn,FID)

strSQL="selectID,NamefromcatalogwhereFatherID="&FID

setrsCatalog=oConn.Execute(strSQL)

%>

<%

DowhilenotrsCatalog.Eof

%>

<%=rsCatalog("Name")%>

<%

Loop

%>

<%

rsCatalog.Close

EndFunction

%>

现在我们来看看如何显示FID下的所有分类。

这需要用到递归算法。

我们只需要在GetChildren函数中简单地对所有ID进行调用:

GetChildren(oConn,Catalog(“ID”))就可以了。

<%

REMoConn---数据库连接,已经打开

REMFID-----当前分类的编号

FunctionGetChildren(oConn,FID)

strSQL="selectNamefromcatalogwhereFatherID="&FID

setrsCatalog=oConn.Execute(strSQL)

%>

<%

DowhilenotrsCatalog.Eof

%>

<%=rsCatalog("Name")%>

<%=GetChildren(oConn,Catalog("ID"))%>

<%

Loop

%>

<%

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)

%>

<%

DowhilenotrsProduct.EOF

%>

<%=rsProduct("Name")%>

<%

Loop

%>

<%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,N

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 法律文书 > 起诉状

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1