版權聲明:本文為博主原創文章,轉載、引用,請署名作者注明文章出處
前言:因工作原因需要設計一個樹形結構表,該樹有如下特點:
1、無唯一根節點
2、每個節點只有1個父節點
3、每個節點可以有多個子節點
4、深度不限
而且在業務需求上,對增刪查都有一定的要求,百度了N久,主要就找到這兩種:
1、一般樹(即parentid標記那種)
http://blog.csdn.net/heyangyi_19940703/article/details/51456064
2、預設左右值樹(這里簡稱左右值樹)
http://blog.csdn.net/monkey_d_meng/article/details/6647488
但在了解后發現一般樹在增刪方面效率高,但在查詢方面差,而左右值樹則相反,這兩種都不適合當前的業務,於是就有以下的設計
一、結構描述
該設計主要參考左右值樹,是在它的基礎上作進一步處理而來,所以我叫它分級預設樹,簡稱分級樹
該樹有以下特征:
1、name(上面的a、b、c)節點標識,可以作外鍵
2、value為該節點在某級的位置標記
3、min和max保存其子節點在其層級的最小位置和最大位置
4、節點的min值<=max值,無子節點時min=max=0,僅有一個子節點時min=max=子節點的value值
5、子孫節點的value值總是在其有子的父級的最小min與最大max之間
6、增加節點時,設新增節點的lv為N(N>1),則影響的深度為2,影響的層數分別為lv=N和lv=N-1層,lv=N層的影響廣度為N層的maxvalue-newvalue,Lv=N-1的影響廣度為N-1層的maxvalue-新增節點的父value
7、刪除節點時,設刪除節點的lv為N(N>1),最小的子孫的lv為M(M>N),則影響的深度為M-N+2層,影響廣度為{(N~M)層(每層的maxvalue-刪除節點minvalue+1)+1}
根據以上設計,下面進行樹形CRUD算法推演
注:6、7特征是在使用過程發現的,可能會有不正確
二、樹形CRUD算法
(1)獲取某節點的子孫節點
(一)、算法
由上面的特征5可以得出查詢算法為:下一層value值在有子的父節點的value最小節點的min值與最大value節點的max值,然后再迭代至下一層,直到min=max=0;
代碼如下:
declare @id int ---傳入的節點id或者使用其他唯一標識都可以
set @id=1 declare @lv int,@min int,@max int,@result int
select @lv=lv,@min=[min],@max=[max] from testtree where id=@id
set @result=0
while(@min<>0) begin set @lv +=1 select * from testtree where lv =@lv and value >=@min and value <=@max ---查找子節點
set @result=@result+@max-@min +1 select @min= min([min]),@max=max([max]) from testtree where lv =@lv and value >=@min and value <=@max and [min]<>0 ---獲取下一級節點的范圍
end
select @result ---子孫數量
遞歸的次數為樹的深度,即樹有多深就遞歸多少次
(二)、效率分析:
① ② ③
注:粉紅線是查詢路徑
上圖分別為一般樹、分級樹、左右值樹的查詢方式圖解,一般樹迭代查詢parentid=id的節點,分級樹迭代查詢每層的邊界值來確定下一層范圍,左右樹通過左右值來確定子孫范圍,所以效率對比為:
一般樹<=分級樹<=左右值樹
(2)為某節點添加子孫節點
(一)、算法
注:粉色為受影響的數據
例:c點添h
代碼如下:
1 declare @name varchar(50) 2 set @name='c' 3 4 declare @lv int,@min int,@max int,@newvalue int,@fvalue int ---@newvalue為h節點的value值,@fvalue為c節點value 5 select @lv=lv,@min=[min],@max=[max],@fvalue =value from testtree where name =@name 6 if(@max>0) ---c節點已有孩子時 7 begin 8 set @newvalue =@max+1 9 end 10 else 11 begin 12 if((select COUNT (*) from testtree where lv=@lv and [max]>0 and value <@fvalue)>0)---看看要c節點有沒有哥哥是有孩子的 13 begin 14 select @newvalue =max([max])+1 from testtree where lv=@lv and value <@fvalue ---有的話h點就排在哥哥的孩子后面 15 end 16 else 17 begin 18 set @newvalue =1 ---沒的話,h就當@lv+1層的大哥 19 end 20 end 21 22 SET XACT_ABORT ON 23 BEGIN TRANSACTION 24 update testtree set [min]+=1,[max]+=1 where lv=@lv and value >@fvalue ---大哥添子,小弟的孩子排名降位 25 update testtree set value +=1 where lv=@lv+1 and value >=@newvalue ---小弟的孩子降位 26 insert into testtree (name,lv,[min],[max],value) values ('h',@lv+1,0,0,@newvalue) 27 if(@min>0) ---更新c點min和max值 28 begin 29 update testtree set [max]=@newvalue where name=@name 30 end 31 else 32 begin 33 update testtree set [min]=@newvalue ,[max]=@newvalue where name =@name 34 end 35 36 COMMIT TRANSACTION 37 SET XACT_ABORT OFF
(二)、效率分析:
一般樹直接插入,無需更新其他點;分級樹插入后需更新插入層以及父層的節點,左右樹在插入后需更新插入節點右邊所有的樹節點(不了解的可以點擊前言處的鏈接去看下,此處不多說);所以效率對比為:
左右樹<=分級樹<一般樹
(3)刪除某節點
(一)、算法
例如刪除c點,如圖示:
注:粉色為刪除點,藍色為受影響點
代碼如下:
1 declare @name varchar(50) 2 set @name ='c' 3 4 declare @lv int,@min int,@max int,@value int,@result int,@nextmin int,@nextmax int,@minvalue int,@pminvalue int,@delenum int 5 select @lv=lv,@nextmin=[min],@nextmax=[max],@value=value from testtree where name =@name 6 set @result =0 7 SET XACT_ABORT ON 8 BEGIN TRANSACTION 9 delete testtree where name =@name ---刪除節點 10 update testtree set value -=1 where lv=@lv and value>@value ---刪除節點所在層的並且在刪除節點右邊的節點的value-1 11 if(@lv>1) ---該節點有父節點時 12 begin 13 if((select [max] from testtree where lv=@lv-1 and [max]>=@value and [min] <=@value)=1) ---該父節點只有1個子節點時 14 begin 15 update testtree set [min]=0,[max]=0 where lv=@lv-1 and [max]>=@value and [min] <=@value 16 end 17 else 18 begin 19 update testtree set [max]-=1 where lv=@lv-1 and [max]>=@value and [min] <=@value 20 end 21 end 22 set @result =1 23 set @minvalue =@value 24 while(@nextmin<>0) ---循環刪除c的子孫 25 begin 26 set @min=@nextmax 27 set @max =@nextmax ---刪除節點的范圍 28 set @pminvalue =@minvalue ---上級刪除節點中的最小value 29 set @lv +=1 30 31 select @minvalue =min(value) , @delenum=COUNT (*) from testtree where lv =@lv and value >=@min and value <=@max ---獲取要刪除節點的最小value和要刪除的節點數 32 select @nextmin = min([min]),@nextmax =max([max]) from testtree where lv =@lv and value >=@min and value <=@max and [min]<>0 ---獲取下一層節點的范圍 33 34 set @result +=@delenum ---刪除的總節點數量 35 36 delete testtree where lv=@lv and value >=@min and value <=@max ----刪除這一層節點 37 update testtree set value -=@delenum where lv=@lv and value >@minvalue ---把這層大於刪除節點的minvalue的節點value值減少 38 update testtree set [min]-=@delenum ,[max]-=@delenum where lv=@lv-1 and value >=@pminvalue ---把上一層的大於父刪除節點minvalue的節點的min、max減少 39 40 end 41 COMMIT TRANSACTION 42 SET XACT_ABORT OFF 43 select @result as delesun ---子孫數量
(二)、效率分析
刪除的影響還是用圖示明顯點,請看下圖:
注:粉色為刪除點,藍色為受影響點
因選擇的點問題所以分級樹和左右值樹效率一樣,但分級樹影響范圍是的是刪除節點的父節點到刪除節點最底層子孫節點的所有右邊節點,而左右值樹影響范圍是的刪除節點的最底層子孫到刪除節點的根節點的所有右邊節點,所以效率比較為:
左右值樹<=分級樹<一般樹
三、總結
通過上面的算法分析,這種設計基本符合我的需求,其它的如節點移動的算法就不多說了。上面的算法應該還可以優化點,有興趣的可以進行優化,最好可以發下出來,不勝感激