路径规划(最短路径)算法C#实现
Posted on 2005-09-29 14:38 zhuweisky
阅读
(5195) 评论(23) 编辑 收藏 引用 网摘
所属分类
: C#专栏
以前空闲的时候用
C#
实现的路径规划算法,今日贴它出来,看大家有没有更好的实现方案。关于路径规划(最短路径)算法的背景知识,大家可以参考《
C++
算法--图算法》一书。
该图算法描述的是这样的场景:图由节点和带有方向的边构成,每条边都有相应的权值,路径规划(最短路径)算法就是要找出从节点
A
到节点
B
的累积权值最小的路径。
首先,我们可以将
“
有向边
”
抽象为
Edge
类:
public
class
Edge
{
public
string
StartNodeID ;
public
string
EndNodeID ;
public
double
Weight ;
//
权值,代价
}
节点则抽象成
Node
类,一个节点上挂着以此节点作为起点的
“
出边
”
表。
public class Node
{
private string iD ;
private ArrayList edgeList ;//Edge
的集合--出边表
public
Node(
string
id )
{
this
.iD = id ;
this
.edgeList =
new
ArrayList() ;
}
property
#region
property
public
string
ID
{
get
{
return
this
.iD ;
}
}
public
ArrayList EdgeList
{
get
{
return
this
.edgeList ;
}
}
#endregion
}
在计算的过程中,我们需要记录到达每一个节点权值最小的路径,这个抽象可以用
PassedPath
类来表示:
///
<summary>
///
PassedPath
用于缓存计算过程中的到达某个节点的权值最小的路径
///
</summary>
public
class
PassedPath
{
private
string
curNodeID ;
private
bool
beProcessed ;
//
是否已被处理
private
double
weight ;
//
累积的权值
private
ArrayList passedIDList ;
//
路径
public
PassedPath(
string
ID)
{
this
.curNodeID = ID ;
this
.weight =
double
.MaxValue ;
this
.passedIDList =
new
ArrayList() ;
this
.beProcessed =
false
;
}
#region
property
public
bool
BeProcessed
{
get
{
return
this
.beProcessed ;
}
set
{
this
.beProcessed = value ;
}
}
public
string
CurNodeID
{
get
{
return
this
.curNodeID ;
}
}
public
double
Weight
{
get
{
return
this
.weight ;
}
set
{
this
.weight = value ;
}
}
public
ArrayList PassedIDList
{
get
{
return
this
.passedIDList ;
}
}
#endregion
}
另外,还需要一个表
PlanCourse
来记录规划的中间结果,即它管理了每一个节点的
PassedPath
。
///
<summary>
///
PlanCourse
缓存从源节点到其它任一节点的最小权值路径=》路径表
///
</summary>
public
class
PlanCourse
{
private
Hashtable htPassedPath ;
#region
ctor
public
PlanCourse(ArrayList nodeList ,
string
originID)
{
this
.htPassedPath =
new
Hashtable() ;
Node originNode =
null
;
foreach
(Node node
in
nodeList)
{
if
(node.ID == originID)
{
originNode = node ;
}
else
{
PassedPath pPath =
new
PassedPath(node.ID) ;
this
.htPassedPath.Add(node.ID ,pPath) ;
}
}
if
(originNode ==
null
)
{
throw
new
Exception("The origin node is not exist !") ;
}
this
.InitializeWeight(originNode) ;
}
private
void
InitializeWeight(Node originNode)
{
if
((originNode.EdgeList ==
null
) ||(originNode.EdgeList.Count == 0))
{
return
;
}
foreach
(Edge edge
in
originNode.EdgeList)
{
PassedPath pPath =
this
[edge.EndNodeID] ;
if
(pPath ==
null
)
{
continue
;
}
pPath.PassedIDList.Add(originNode.ID) ;
pPath.Weight = edge.Weight ;
}
}
#endregion
public
PassedPath
this
[
string
nodeID]
{
get
{
return
(PassedPath)
this
.htPassedPath[nodeID] ;
}
}
}
在所有的基础构建好后,路径规划算法就很容易实施了,该算法主要步骤如下:
(1)
用一张表
(PlanCourse)
记录源点到任何其它一节点的最小权值,初始化这张表时,如果源点能直通某节点,则权值设为对应的边的权,否则设为
double.MaxValue
。
(2)
选取没有被处理并且当前累积权值最小的节点
TargetNode
,用其边的可达性来更新到达其它节点的路径和权值(如果其它节点
经此节点后权值变小则更新,否则不更新),然后标记
TargetNode
为已处理。
(3)
重复
(2)
,直至所有的可达节点都被处理一遍。
(4)
从
PlanCourse
表中获取目的点的
PassedPath
,即为结果。
下面就来看上述步骤的实现,该实现被封装在
RoutePlanner
类中:
///
<summary>
///
RoutePlanner
提供图算法中常用的路径规划功能。
///
2005.09.06
///
</summary>
public
class
RoutePlanner
{
public
RoutePlanner()
{
}
#region
Paln
//
获取权值最小的路径
public
RoutePlanResult Paln(ArrayList nodeList ,
string
originID ,
string
destID)
{
PlanCourse planCourse =
new
PlanCourse(nodeList ,originID) ;
Node curNode =
this
.GetMinWeightRudeNode(planCourse ,nodeList ,originID) ;
#region
计算过程
while
(curNode !=
null
)
{
PassedPath curPath = planCourse[curNode.ID] ;
foreach
(Edge edge
in
curNode.EdgeList)
{
PassedPath targetPath = planCourse[edge.EndNodeID] ;
double
tempWeight = curPath.Weight + edge.Weight ;
if
(tempWeight < targetPath.Weight)
{
targetPath.Weight = tempWeight ;
targetPath.PassedIDList.Clear() ;
for
(
int
i=0 ;i<curPath.PassedIDList.Count ;i++)
{
targetPath.PassedIDList.Add(curPath.PassedIDList[i].ToString()) ;
}
targetPath.PassedIDList.Add(curNode.ID) ;
}
}
//
标志为已处理
planCourse[curNode.ID].BeProcessed =
true
;
//
获取下一个未处理节点
curNode =
this
.GetMinWeightRudeNode(planCourse ,nodeList ,originID) ;
}
#endregion
//
表示规划结束
return
this
.GetResult(planCourse ,destID) ;
}
#endregion
#region
private method
#region
GetResult
//
从
PlanCourse
表中取出目标节点的
PassedPath
,这个
PassedPath
即是规划结果
private
RoutePlanResult GetResult(PlanCourse planCourse ,
string
destID)
{
PassedPath pPath = planCourse[destID] ;
if
(pPath.Weight ==
int
.MaxValue)
{
RoutePlanResult result1 =
new
RoutePlanResult(
null
,
int
.MaxValue) ;
return
result1 ;
}
string
[] passedNodeIDs =
new
string
[pPath.PassedIDList.Count] ;
for
(
int
i=0 ;i<passedNodeIDs.Length ;i++)
{
passedNodeIDs[i] = pPath.PassedIDList[i].ToString() ;
}
RoutePlanResult result =
new
RoutePlanResult(passedNodeIDs ,pPath.Weight) ;
return
result ;
}
#endregion
#region
GetMinWeightRudeNode
//
从
PlanCourse
取出一个当前累积权值最小,并且没有被处理过的节点
private
Node GetMinWeightRudeNode(PlanCourse planCourse ,ArrayList nodeList ,
string
originID)
{
double
weight =
double
.MaxValue ;
Node destNode =
null
;
foreach
(Node node
in
nodeList)
{
if
(node.ID == originID)
{
continue
;
}
PassedPath pPath = planCourse[node.ID] ;
if
(pPath.BeProcessed)
{
continue
;
}
if
(pPath.Weight < weight)
{
weight = pPath.Weight ;
destNode = node ;
}
}
return
destNode ;
}
#endregion
#endregion
}
2006.05.22
应众多朋友要求,下面给出一个简单示例:
RoutePlanner.Plan
过程详解:
(1)
用一张表
(PlanCourse)
记录源点到任何其它一节点的最小权值,初始化这张表时,如果源点能直通某节点,则权值设为对应的
边的权,否则设为
double.MaxValue
(2)
选取没有被处理并且当前累积权值最小的节点
TargetNode
,用其边的可达性来更新到达其它节点的路径和权值(如果其它节点
经此节点后权值变小则更新,否则不更新),然后标记
TargetNode
为已处理
(3)
重复
(2)
,直至所有的可达节点都被处理一遍。
(4)
从
PlanCourse
表中获取目的点的
PassedPath
,即为结果。
[STAThread]
static
void
Main(
string
[] args)
{
ArrayList nodeList =
new
ArrayList() ;
//***************** B Node *******************
Node aNode =
new
Node("A") ;
nodeList.Add(aNode) ;
//A -> B
Edge aEdge1 =
new
Edge() ;
aEdge1.StartNodeID = aNode.ID ;
aEdge1.EndNodeID = "B" ;
aEdge1.Weight = 10 ;
aNode.EdgeList.Add(aEdge1) ;
//A -> C
Edge aEdge2 =
new
Edge() ;
aEdge2.StartNodeID = aNode.ID ;
aEdge2.EndNodeID = "C" ;
aEdge2.Weight = 20 ;
aNode.EdgeList.Add(aEdge2) ;
//A -> E
Edge aEdge3 =
new
Edge() ;
aEdge3.StartNodeID = aNode.ID ;
aEdge3.EndNodeID = "E" ;
aEdge3.Weight = 30 ;
aNode.EdgeList.Add(aEdge3) ;
//***************** B Node *******************
Node bNode =
new
Node("B") ;
nodeList.Add(bNode) ;
//B -> C
Edge bEdge1 =
new
Edge() ;
bEdge1.StartNodeID = bNode.ID ;
bEdge1.EndNodeID = "C" ;
bEdge1.Weight = 5 ;
bNode.EdgeList.Add(bEdge1) ;
//B -> E
Edge bEdge2 =
new
Edge() ;
bEdge2.StartNodeID = bNode.ID ;
bEdge2.EndNodeID = "E" ;
bEdge2.Weight = 10 ;
bNode.EdgeList.Add(bEdge2) ;
//***************** C Node *******************
Node cNode =
new
Node("C") ;
nodeList.Add(cNode) ;
//C -> D
Edge cEdge1 =
new
Edge() ;
cEdge1.StartNodeID = cNode.ID ;
cEdge1.EndNodeID = "D" ;
cEdge1.Weight = 30 ;
cNode.EdgeList.Add(cEdge1) ;
//***************** D Node *******************
Node dNode =
new
Node("D") ;
nodeList.Add(dNode) ;
//***************** C Node *******************
Node eNode =
new
Node("E") ;
nodeList.Add(eNode) ;
//C -> D
Edge eEdge1 =
new
Edge() ;
eEdge1.StartNodeID = eNode.ID ;
eEdge1.EndNodeID = "D" ;
eEdge1.Weight = 20 ;
eNode.EdgeList.Add(eEdge1) ;
RoutePlanner planner =
new
RoutePlanner() ;
RoutePlanResult result = planner.Paln(nodeList ,"A" ,"D") ;
planner =
null
;
}