|
用中值排序基數(shù)法實現(xiàn)樹狀結構
在BBS的編寫中,經(jīng)常有人問怎樣實現(xiàn)樹狀結構?一個比較不負責任的回答是:使用遞歸算法。當然,遞歸是一個可行的辦法(二叉樹的歷遍也好象只能使用遞歸算法),但對于BBS來說,這樣做勢必要進行大量的Sql查詢(雖然可以使用存儲過程來做,但要從根本上加快速度,則應該考慮更快的算法)。 下面給出一個可行的徹底屏棄遞的實現(xiàn)樹狀結構的算法。
下面給出另一種使用“使用中值排序基數(shù)法”實現(xiàn)樹狀結構: 一、主要思想:增加一個排序基數(shù)字段ordernum,回復同一根貼的貼子中插入貼子時,排序基數(shù)ordernum取兩者的中值。 為了敘述的簡潔,在此只討論與樹狀結構有關的字段。
在表中增加三個冗余字段,rootid——用于記錄根id,deep——用于記錄回復的深度(為0時表示根貼),ordernum——排序基數(shù)(關鍵所在)。
表forum與(只列與樹狀結構有關的字段): id rootid deepordernum 其中id、rootid、deep均為int型(deep可為tinyint型),ordernum為float型。
例:(在此為了簡單,使用一個小的起始排序基數(shù),在實際應用中,應使用較大的起始基數(shù),且應取2的整數(shù)次冪,如65536=2^16,下面所說的排序均指按ordernum從小到大排序)。 id rootiddeepordernum 1000 211 64 ______________________________ 311 32回復第1貼,取1、2基數(shù)的中值即(0+64)/2
排序后結果為: id rootiddeepordernum 1000 311 32 211 64 ______________________________ 412 48 回復第3貼,取3、2的基數(shù)中值即(32+64)/2
排序后結果為: id rootiddeepordernum 1000 311 32 412 48 211 64 ______________________________ 513 56回復第4貼,取4、2的基數(shù)中值即(48+64)/2
排序后的結果為: id rootiddeepordernum 1000 311 32 412 48 513 56 211 64 ______________________________ 612 40回復第3貼,取3、4的基數(shù)中值即(32+48)/2
排序后的結果為: id rootiddeepordernum 1000 311 32 612 40 412 48 513 56 211 64
這樣排序基數(shù)ordernum與回復深度deep一起就實現(xiàn)了如下的樹狀結構: id 1 3 6 4 5 2
二、插入的實現(xiàn)(如何確定排序基數(shù),下面所指貼子均為同一根下的子貼) (一)根ordernum定為0 (二)第一條回復貼子基數(shù)定為2的整數(shù)次冪(如65536=2^16,可取更大的數(shù)) (三)回復最后一條貼子時,基數(shù)取最后一貼的基數(shù)ordernum再加上2的整數(shù)次冪(同上) (四)回復中間的貼子時,基數(shù)ordernum取前后貼子的基數(shù)中值
三、刪除的實現(xiàn) 刪除貼子(剪枝)時,只需找出下一個回復深度deep小于或等于要刪貼子的回復深度(deep)的貼子,然后將基數(shù)ordernum位于兩個貼子基數(shù)之間的貼子刪除即可實現(xiàn)剪枝。 如上例子中,要刪除3貼(基數(shù)為32)下的子枝,由于3的深度為1,下一個深度小于或等于1的貼子為2貼(它的基數(shù)為64),則只需刪除基數(shù)在32至64間(64除外)的貼子就行了。也就是刪除了3、6、4、5貼。要刪其它亦然。
四、顯示的實現(xiàn) 只需執(zhí)行select * from forum order by rootid+id-sign(rootid)*id desc,ordernum,然后結合deep就可實現(xiàn)樹狀的顯示。
五、具體實現(xiàn)方法(以存儲過程為例)
加貼存儲過程:(省略注冊用戶檢測以及積分部分內(nèi)容)
CREATE PROCEDURE [add] @keyid int,@message varchar(50) OUTPUT———keyid為回復的貼子id號,如果是新貼則為0,@message為出錯信息 AS IF (@keyid=0) INSERT INTO forum (rootid,deep,ordernum,……) values(0,0,0,……) ELSE BEGIN DECLARE @rootid int,@id int,@deep int,@begnum float,@endnum float,@ordernum float SELECT @rootid=0,@id=0,@deep=0,@begnum=0,@endnum=0,@ordernum=0 SELECT @rootid=rootid,@id=id,@begnum=ordernum,@deep=deep from forum where id=@keyid IF (@id=0) BEGIN SELECT @message='要回復的貼子已經(jīng)被刪除!' return END ELSE BEGIN IF (@rootid=0) SELECT @rootid=@id——回復的是根貼,取其id為新加貼的rootid SELECT @endnum=ordernum where rootid=@rootid and ordernum>@begnum order by ordernum IF (@endnum=0) SELECT @ordernum=@begnum+65536 ——回復的是最后一貼 ELSE SELECT @ordernum=(@begnum+@endnum)/2——關鍵,取排序基數(shù)中值 INSERT into forum (rootid,deep,ordernum,……) values(@rootid,@deep+1,@ordernum,……) END END Select @message='成功' return
剪枝存儲過程:(省略注冊用戶檢測以及積分部分內(nèi)容) CREATE PROCEDURE [del] @keyid int,@message varchar(50) OUTPUT———keyid為要刪除的貼子id號,如果是新貼則為0,@message為出錯信息 AS DECLARE @rootid int,@id int,@deep int,@begnum float,@endnum float SELECT @rootid=0,@deep=0,@begnum=0,@endnum=0,@id=0 SELECT @id=id,@begnum=ordernum,@rootid=rootid,@deep=deep from forum where id=@keyid IF (@id=0) BEGIN SELECT @message='該貼子不存在!" return END ELSE BEGIN SELECT @endnum=ordernum from forum where rootid=@rootid and deep<=@deep and ordernum>@begnum order by ordernum IF (@endnum=0)——要刪除的是最后一個子枝 DELETE FROM forum where ordernum>=@begnum and (rootid=@rootid or id=@rootid) ELSE DELETE FROM forum where ordernum>=@begnum and ordernum<@endnum and (rootid=@rootid or id=@rootid) END
顯示存儲過程(略)
總結:由于省去了childnum字段,因此如果想要知道根貼(或子貼)有多少個子貼,則需使用統(tǒng)計方法或增加對應的字段記錄,該問題可不列為樹狀結構討論之列。
賣一下廣告先:歡迎訪問我的個人主頁http://swuse.yeah.net
|