開始
這是去年的問題了,今天在整理郵件的時候才發(fā)現(xiàn)這個問題,感覺頂有意思的,特記錄下來。
在表RelationGraph中,有三個字段(ID,Node,RelatedNode),其中Node和RelatedNode兩個字段描述兩個節(jié)點(diǎn)的連接關(guān)系;現(xiàn)在要求,找出從節(jié)點(diǎn)"p"至節(jié)點(diǎn)"j",最短路徑(即經(jīng)過的節(jié)點(diǎn)最少)。

圖1.
解析:
了能夠更好的描述表RelationGraph中字段Node和 RelatedNode的關(guān)系,我在這里特意使用一個圖形來描述,
如圖2. 

圖2.
在圖2,可清晰的看出各個節(jié)點(diǎn)直接如何相連,也可以清楚的看出節(jié)點(diǎn)"p"至節(jié)點(diǎn)"j"的的幾種可能路徑。

從上面可以看出第2種可能路徑,經(jīng)過的節(jié)點(diǎn)最少。
為了解決開始的問題,我參考了兩種方法,
第1方法是,
參考單源最短路徑算法:

第2方法是,
針對第1種方法的改進(jìn),就是采用多源點(diǎn)方法,這里就是以節(jié)點(diǎn)"p"和節(jié)點(diǎn)"j"為中心向外層擴(kuò)展,直到兩圓外切點(diǎn),如圖4. :

圖4.
實現(xiàn):
在接下來,我就描述在SQL Server中,如何實現(xiàn)。當(dāng)然我這里采用的前面說的第2種方法,以"P"和"J"為始點(diǎn)像中心外層層擴(kuò)展。
這里提供有表RelactionGraph的create& Insert數(shù)據(jù)的腳本:
復(fù)制代碼 代碼如下:
use TestDB
go
if object_id('RelactionGraph') Is not null drop table RelactionGraph
create table RelactionGraph(ID int identity,Item nvarchar(50),RelactionItem nvarchar(20),constraint PK_RelactionGraph primary key(ID))
go
create nonclustered index IX_RelactionGraph_Item on RelactionGraph(Item) include(RelactionItem)
create nonclustered index IX_RelactionGraph_RelactionItem on RelactionGraph(RelactionItem) include(Item)
go
insert into RelactionGraph (Item, RelactionItem ) values
('a','b'),('a','c'),('a','d'),('a','e'),
('b','f'),('b','g'),('b','h'),
('c','i'),('c','j'),
('f','k'),('f','l'),
('k','o'),('k','p'),
('o','i'),('o','l')
go
復(fù)制代碼 代碼如下:
use TestDB 
go 
--Procedure: 
if object_id('up_GetPath') Is not null 
    Drop proc up_GetPath 
go 
create proc up_GetPath 
( 
    @Node nvarchar(50), 
    @RelatedNode nvarchar(50) 
) 
As 
set nocount on 
declare 
    @level smallint =1, --當(dāng)前搜索的深度 
    @MaxLevel smallint=100, --最大可搜索深度 
    @Node_WhileFlag bit=1, --以@Node作為中心進(jìn)行搜索時候,作為能否循環(huán)搜索的標(biāo)記 
    @RelatedNode_WhileFlag bit=1 --以@RelatedNode作為中心進(jìn)行搜索時候,作為能否循環(huán)搜索的標(biāo)記 
--如果直接找到兩個Node存在直接關(guān)系就直接返回 
if Exists(select 1 from RelationGraph where (Node=@Node And RelatedNode=@RelatedNode) or (Node=@RelatedNode And RelatedNode=@Node) ) or @Node=@RelatedNode 
begin 
    select convert(nvarchar(2000),@Node + ' --> '+ @RelatedNode) As RelationGraphPath,convert(smallint,0) As StopCount 
    return 
end 
-- 
if object_id('tempdb..#1') Is not null Drop Table #1 --臨時表#1,存儲的是以@Node作為中心向外擴(kuò)展的各節(jié)點(diǎn)數(shù)據(jù) 
if object_id('tempdb..#2') Is not null Drop Table #2 --臨時表#2,存儲的是以@RelatedNode作為中心向外擴(kuò)展的各節(jié)點(diǎn)數(shù)據(jù) 
create table #1( 
    Node nvarchar(50),--相對源點(diǎn) 
    RelatedNode nvarchar(50), --相對目標(biāo) 
    Level smallint --深度 
    ) 
create table #2(Node nvarchar(50),RelatedNode nvarchar(50),Level smallint) 
insert into #1 ( Node, RelatedNode, Level ) 
    select Node, RelatedNode, @level from RelationGraph a where a.Node =@Node union --正向:以@Node作為源查詢 
    select RelatedNode, Node, @level from RelationGraph a where a.RelatedNode = @Node --反向:以@Node作為目標(biāo)進(jìn)行查詢 
set @Node_WhileFlag=sign(@@rowcount) 
insert into #2 ( Node, RelatedNode, Level ) 
    select Node, RelatedNode, @level from RelationGraph a where a.Node =@RelatedNode union --正向:以@RelatedNode作為源查詢 
    select RelatedNode, Node, @level from RelationGraph a where a.RelatedNode = @RelatedNode --反向:以@RelatedNode作為目標(biāo)進(jìn)行查詢 
set @RelatedNode_WhileFlag=sign(@@rowcount) 
--如果在表RelationGraph中找不到@Node 或 @RelatedNode 數(shù)據(jù),就直接跳過后面的While過程 
if not exists(select 1 from #1) or not exists(select 1 from #2) 
begin 
    goto While_Out 
end 
 
while not exists(select 1 from #1 a inner join #2 b on b.RelatedNode=a.RelatedNode) --判斷是否出現(xiàn)切點(diǎn) 
and (@Node_WhileFlag|@RelatedNode_WhileFlag)>0 --判斷是否能搜索 
And @level<@MaxLevel --控制深度 
begin 
    if @Node_WhileFlag >0 
    begin
insert into #1 ( Node, RelatedNode, Level ) 
--正向 
select a.Node,a.RelatedNode,@level+1 
From RelationGraph a 
where exists(select 1 from #1 where RelatedNode=a.Node And Level=@level) And 
Not exists(select 1 from #1 where Node=a.Node)
union 
--反向 
select a.RelatedNode,a.Node,@level+1 
From RelationGraph a 
where exists(select 1 from #1 where RelatedNode=a.RelatedNode And Level=@level) And 
Not exists(select 1 from #1 where Node=a.RelatedNode) 
set @Node_WhileFlag=sign(@@rowcount) 
    end 
    if @RelatedNode_WhileFlag >0 
    begin
insert into #2 ( Node, RelatedNode, Level ) 
--正向 
select a.Node,a.RelatedNode,@level+1 
From RelationGraph a 
where exists(select 1 from #2 where RelatedNode=a.Node And Level=@level) And 
Not exists(select 1 from #2 where Node=a.Node) 
union 
--反向 
select a.RelatedNode,a.Node,@level+1 
From RelationGraph a 
where exists(select 1 from #2 where RelatedNode=a.RelatedNode And Level=@level) And 
Not exists(select 1 from #2 where Node=a.RelatedNode) 
set @RelatedNode_WhileFlag=sign(@@rowcount) 
    end 
    select @level+=1 
end 
While_Out: 
--下面是構(gòu)造返回的結(jié)果路徑 
if object_id('tempdb..#Path1') Is not null Drop Table #Path1 
if object_id('tempdb..#Path2') Is not null Drop Table #Path2 
;with cte_path1 As 
( 
select a.Node,a.RelatedNode,Level,convert(nvarchar(2000),a.Node+' -> '+a.RelatedNode) As RelationGraphPath,Convert(smallint,1) As PathLevel From #1 a where exists(select 1 from #2 where RelatedNode=a.RelatedNode) 
union all 
select b.Node,a.RelatedNode,b.Level,convert(nvarchar(2000),b.Node+' -> '+a.RelationGraphPath) As RelationGraphPath ,Convert(smallint,a.PathLevel+1) As PathLevel 
    from cte_path1 a 
inner join #1 b on b.RelatedNode=a.Node 
and b.Level=a.Level-1 
) 
select * Into #Path1 from cte_path1 
;with cte_path2 As 
( 
select a.Node,a.RelatedNode,Level,convert(nvarchar(2000),a.Node) As RelationGraphPath,Convert(smallint,1) As PathLevel From #2 a where exists(select 1 from #1 where RelatedNode=a.RelatedNode) 
union all 
select b.Node,a.RelatedNode,b.Level,convert(nvarchar(2000),a.RelationGraphPath+' -> '+b.Node) As RelationGraphPath ,Convert(smallint,a.PathLevel+1) 
    from cte_path2 a 
inner join #2 b on b.RelatedNode=a.Node 
and b.Level=a.Level-1 
) 
select * Into #Path2 from cte_path2 
;with cte_result As 
( 
select a.RelationGraphPath+' -> '+b.RelationGraphPath As RelationGraphPath,a.PathLevel+b.PathLevel -1 As StopCount,rank() over(order by a.PathLevel+b.PathLevel) As Result_row 
    From #Path1 a 
inner join #Path2 b on b.RelatedNode=a.RelatedNode 
and b.Level=1 
    where a.Level=1 
)
select distinct RelationGraphPath,StopCount From cte_result where Result_row=1 
go
下面是存儲過程的執(zhí)行:
復(fù)制代碼 代碼如下:
新聞熱點(diǎn)
疑難解答
圖片精選