用中值排序基數法實現樹狀結構 (轉)

worldblog發表於2007-12-23
用中值排序基數法實現樹狀結構 (轉)[@more@]

  在BBS的編寫中,經常有人問怎樣實現樹狀結構?一個比較不負責任的回答是:使用遞迴演算法。當然,遞迴是一個可行的辦法


(二叉樹的歷遍也好象只能使用遞迴演算法),但對於BBS來說,這樣做勢必要進行大量的查詢(雖然可以使用過程來做,但要從根本上加度,則應該考慮更快的演算法)。


下面給出一個可行的徹底摒棄遞的實現樹狀結構的演算法。


  下面給出另一種使用“使用中值排序基數法”實現樹狀結構:


一、主要思想:增加一個排序基數字段ordernum,回覆同一根貼的貼子中插入貼子時,排序基數ordernum取兩者的中值。


  為了敘述的簡潔,在此只討論與樹狀結構有關的欄位。


在表中增加三個冗餘欄位,id——用於記錄根id,deep——用於記錄回覆的深度(為0時表示根貼),ordernum——排序基數(關鍵所在)。


表forum與(只列與樹狀結構有關的欄位):id  rootid  deep  ordernum其中id、rootid、deep均為int型(deep可為tinyint型),ordernum為float型。


例:(在此為了簡單,使用一個小的起始排序基數,在實際應用中,應使用較大的起始基數,且應取2的整數次冪,如65536=2^16,下面所說的排序均指按ordernum從小到大排序)。


id  rootid  deep  ordernum


1  0  0  0


2  1  1  64


______________________________


3  1  1  32  回覆第1貼,取1、2基數的中值即(0+64)/2


排序後結果為:


id  rootid  deep  ordernum


1  0  0  0


3  1  1  32


2  1  1  64


______________________________


4  1  2  48  回覆第3貼,取3、2的基數中值即(32+64)/2


排序後結果為:


id  rootid  deep  ordernum


1  0  0  0


3  1  1  32


4  1  2  48


2  1  1  64


______________________________


5  1  3  56  回覆第4貼,取4、2的基數中值即(48+64)/2


排序後的結果為:


id  rootid  deep  ordernum


1  0  0  0


3  1  1  32


4  1  2  48


5  1  3  56


2  1  1  64


______________________________


6  1  2  40  回覆第3貼,取3、4的基數中值即(32+48)/2


排序後的結果為:


id  rootid  deep  ordernum


1  0  0  0


3  1  1  32


6  1  2  40


4  1  2  48


5  1  3  56


2  1  1  64


這樣排序基數ordernum與回覆深度deep一起就實現瞭如下的樹狀結構:


id


1


  3


  6


  4


  5


2


二、插入的實現(如何確定排序基數,下面所指貼子均為同一根下的子貼)


(一)根ordernum定為0


(二)第一條回覆貼子基數定為2的整數次冪(如65536=2^16,可取更大的數)


(三)回覆最後一條貼子時,基數取最後一貼的基數ordernum再加上2的整數次冪(同上)


(四)回覆中間的貼子時,基數ordernum取前後貼子的基數中值


三、刪除的實現


  刪除貼子(剪枝)時,只需找出下一個回覆深度deep小於或等於要刪貼子的回覆深度(deep)的貼子,然後將基數ordernum位於兩個貼子基數之間的貼子刪除即可實現剪枝。


  如上例子中,要刪除3貼(基數為32)下的子枝,由於3的深度為1,下一個深度小於或等於1的貼子為2貼(它的基數為64),則只需刪除基數在32至64間(64除外)的貼子就行了。也就是刪除了3、6、4、5貼。要刪其它亦然。


四、顯示的實現


  只需 * from forum order by rootid+id-sign(rootid)*id desc,ordernum,然後結合deep就可實現樹狀的顯示。


五、具體實現方法(以儲存過程為例)


加貼儲存過程:(省略註冊檢測以及積分部分內容)


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,@bem 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='要回復的帖子已經被刪除!'


  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  ——關鍵,取排序基數中值


  INSERT into forum (rootid,deep,ordernum,……) values(@rootid,@deep+1,@ordernum,……)


  END


  END


  Select @message='成功'


  return


剪枝儲存過程:(省略註冊使用者檢測以及積分部分內容)


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

  END


顯示儲存過程(略)


總結:由於省去了childnum欄位,因此如果想要知道根貼(或子貼)有多少個子貼,則需使用統計方法或增加對應的欄位記錄,該問題可不列為樹狀結構討論之列。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-995560/,如需轉載,請註明出處,否則將追究法律責任。

相關文章