路径规划(最短路径)算法C#实现

来源:互联网 发布:java程序员书籍推荐 编辑:程序博客网 时间:2024/05/18 03:49

路径规划(最短路径)算法C#实现

这个算法是错误的,转发一段时间,也照着写了代码(C++版本的)。后来和同事讨论时发现这个在逻辑上有漏洞。

我后来和会算法的哥们学习了算法,写了一个更加简单易懂的,最重要是正确的算法。

老说有时间写blog,这都又半年了……今天刚刚看到,这篇竟然有1k以上的阅读,真是害人不浅。

赶紧来声明一下。还是第一次遇到转发这么多的文章是错误的。

再次声明,我认为这篇算法是错误的。我以前转发的,现在发现是错误的,逻辑的深度和广度他只算了一个方面,逻辑有漏洞。

要是哪位认为没有漏洞,赶快来告诉我,我对这篇算法一直感觉很晕,总之,他的实现方法是很不简单明白的,很繁琐。

  

  以前空闲的时候用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
    }

    
    在计算的过程中,我们需要记录到达每一个节点权值最小的路径,这个抽象可以用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 ;
        }

 

参考文章:
迪杰斯特拉算法——百度百科
平:这个里面的GIF动画很形象生动好理解。
http://baike.baidu.com/link?url=6OZpXnxKlemD7jm0xegf_mEn_xoYW8ThEI6PBlFyOxHDQCWVJp-Y9lJ97MVUi6p-rzrKH4mAM3aj4Ss87GNW1a


路径规划(最短路径)算法C#实现
平:细致的讲解了C#代码的实现。每个类的功能注解很清楚。
但是总感觉各个类的功能不明确。应该可以细分。有时间我写一个。不过很难,前一阵去洗分了,感觉细分好混乱啊。
http://zhuweisky.cnblogs.com/archive/2005/09/29/246677.html

1 0
原创粉丝点击