关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » SQL Server »SQL Server实现最短路径的搜索算法

    SQL Server实现最短路径的搜索算法

    2015-09-18 00:00:00 出处:ITJS
    分享

    这是去年的问题了,今天在整理邮件的时候才发现这个问题,感觉顶有意思的,特记录下来。

    图1.

    解析

    为了能够更好的描述表RelationGraph中字段Node和 RelatedNode的关系,我在这里特意使用一个图形来描述,如图2.

    图2.

    在图2,可清晰的看出各个节点直接如何相连,也可以清楚的看出节点"p"至节点"j"的的几种可能路径。

    从上面可以看出第2种可能路径,经过的节点最少。

    为了解决开始的问题,我参考了两种方法,

    第1方法是,

    参考单源最短路径算法:Dijkstra(迪杰斯特拉)算法,主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。

    图3.

    第2方法是,

    针对第1种方法的改进,就是采用多源点方法,这里就是以节点"p"和节点"j"为中心向外层扩展,直到两圆外切点,如图4. :

    图4.

    实现

    在接下来,我就描述在SQL Server中,如何实现。当然我这里采用的前面说的第2种方法,以"P"和"J"为始点像中心外层层扩展。

    这里提供有表RelactionGraph的create& Insert数据的脚本:

    use TestDB     go if object_id('RelactionGraph') Is not null drop table RelactionGraph create table RelactionGraph(ID int identity,Item nvarchar(50),RelactionItemnvarchar(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 

    编写一个存储过程up_GetPath

    use TestDB go exec dbo.up_GetPath         @Node = 'p', @RelatedNode = 'j' go 

    上面的存储过程,主要分为两大部分,第1部分是实现如何搜索,第2部分实现如何构造返回结果。其中第1部分的代码根据前面的方法2,通过@Node 和 @RelatedNode 两个节点向外层搜索,每次搜索返回的节点都保存至临时表#1和#2,再判断临时表#1和#2有没有出现切点,假如出现就说明已找到最短的路径(经过多节点数最少),否则就继续循环搜索,直到循环至最大的搜索深度(@MaxLevel smallint=100)或找到切点。要是到100层都没搜索到切点,将放弃搜索。这里使用最大可搜索深度@MaxLevel,目的是控制由于数据量大可能会导致性能差,因为在这里数据量与搜索性能成反比。代码中还说到一个正向和反向搜索,主要是相对Node 和 RelatedNode来说,它们两者互为参照对象,进行向外搜索使用。

    下面是存储过程的执行:

    你可以根据需要来,赋予@Node 和 @RelatedNode不同的值。

    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, --当前搜索的深度     @MaxLevel smallint=100, --最大可搜索深度     @Node_WhileFlag bit=1, --以@Node作为中心进行搜索时候,作为能否循环搜索的标记     @RelatedNode_WhileFlag bit=1 --以@RelatedNode作为中心进行搜索时候,作为能否循环搜索的标记 --假如直接找到两个Node存在直接关系就直接返回 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) AsRelationGraphPath,convert(smallint,0) As StopCount     return end -- if object_id('tempdb..#1') Is not null Drop Table #1 --临时表#1,存储的是以@Node作为中心向外扩展的各节点数据 if object_id('tempdb..#2') Is not null Drop Table #2 --临时表#2,存储的是以@RelatedNode作为中心向外扩展的各节点数据 create table #1(     Node nvarchar(50),--相对源点     RelatedNode nvarchar(50), --相对目标     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作为目标进行查询 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作为目标进行查询 set @RelatedNode_WhileFlag=sign(@@rowcount) --假如在表RelationGraph中找不到@Node 或 @RelatedNode 数据,就直接跳过后面的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) --判断是否出现切点      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 AndLevel=@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 AndLevel=@level) And                     Not exists(select 1 from #2 where Node=a.RelatedNode)         set @RelatedNode_WhileFlag=sign(@@rowcount)   end     select @level+=1 end While_Out: --下面是构造返回的结果路径 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) AsRelationGraphPath,Convert(smallint,1) As PathLevel From #1 a where exists(select 1 from #2where 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) AsRelationGraphPath,Convert(smallint,1) As PathLevel From #2 a where exists(select 1 from #1where 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 AsRelationGraphPath,a.PathLevel+b.PathLevel -1 As StopCount,rank() over(order bya.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 

    扩展

    前面的例子,可扩展至城市的公交路线,提供两个站点,搜索经过这两个站点最少站点公交路线;可以扩展至社区的人际关系的搜索,如一个人与另一个人想认识,那么他们直接要经过多少个人才可以。除了人与人直接有直接的朋友、亲戚关联,还可以通过人与物有关联找到人与人关联,如几个作家通过出版一个本,那么就说明这几个人可以通过某一本书的作者列表中找到他们存在共同出版书籍的关联,这为搜索两个人认识路径提供参考。这问题可能会非常大复杂,但可以这样的扩展。

    小结

    这里只是找两个节点的所有路径中,节点数最少的路径,在实际的应用中,可能会碰到比这里更复杂的情况。在其他的环境或场景可能会带有长度,时间,多节点,多作用域等一些信息。无论如何,一般都要参考一些原理,算法来实现。

    原文链接:http://www.cnblogs.com/wghao/archive/2013/04/23/3036965.html

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    商业智能成CIO优先关注点 技术落地方显成效(1)

    用linux安装MySQL时产生问题破解

    JAVA中关于Map的九大问题

    windows 7旗舰版无法使用远程登录如何开启telnet服务

    Android View 事件分发机制详解

    MySQL用户变量的用法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。