最短路径问题的A*算法实现及应用(原创)

       最短路径问题是网络GIS中常见的问题,是许多应用功能的基础,可以实现的算法很多,本文讨论的是使用效率很高的 A*寻路算法实现。
        参考两篇A*算法的经典文章:
                URL:http://www.gamedev.net/reference/articles/article2003.asp
                URL:http://www.policyalmanac.org/games/binaryHeaps.htm
        译文可以在网上找到,有需要可以查阅先。

A*算法的精髓概括如下:
        我们判断最短路径的依据为 F = G + H,这里,G=从起点A到一个可行的结点B的权值开销, H=从当前结点到目的结点的估计权值开销。这种方式常叫做试探法,其实之所以叫做试探法是因为这只是一个猜测。在找到路径之前我们实际上并不知道实际的距离,因为从当前结点到终点存在多种路径,不同的计算H值的方法,在分析过程中是不完全一样的,最终结果路径也可能不一样(当路径权值相同时,我们的最短路径可以不一样)。本文中用两结点间直线距离计算H值。我们采用两个列表(本文用hash表实现),开放列表和关闭列表,开放列表存放待分析结点,关闭列表存放已分析结点。

1.  将开始节点放入开放列表(开始节点的F和G值都视为0);

2.  重复以下步骤:
          a     在开放列表中查找具有最小F值的节点,并把查找到的节点作为当前节点;
          b     把当前节点从开放列表删除, 加入到封闭列表;
          c     对当前节点相邻的每一个节点依次执行以下步骤:
   <1>.     如果该相邻节点不可通行或者该相邻节点已经在封闭列表中,则什么操作也不执行,继续检验下一个节点;
   <2>.     如果该相邻节点不在开放列表中,则将该节点添加到开放列表中, 并将该相邻节点的父节点设为当前节点,同时保存该相邻节点的G和F值;
   <3>.     如果该相邻节点在开放列表中, 则判断若经由当前节点到达该相邻节点的G值是否小于原来保存的G值,若小于,则将该相邻节点的父节点设为当前节点,并重新设置该相邻节点的G和F值.
           d    循环结束条件:
当终点节点被加入到开放列表作为待检验节点时, 表示路径被找到,此时应终止循环;
或者当开放列表为空,表明已无可以添加的新节点,而已检验的节点中没有终点节点则意味着路径无法被找到,此时也结束循环;
3.     从终点节点开始沿父节点遍历, 直到起点,遍历所得的节点就是最后得到的路径;

        “最短路径”问题只是一般的说法,我们可以根据权值代表的意义扩展开来,解决“最优”、“最小”、“最长”等等问题,进一步解决“次优”类问题,这其中就有运筹学的概念了。我们可以在此基础上应用到我们实际项目的问题当中,后面将有介绍。
          A*算法中影响效率的关键应该是在每次选择下一路径结点比较权值的地方,我们采用参考文章中推荐的二叉堆结构,堆顶为最小权值边。实现代码如下:

  1 ContractedBlock.gif ExpandedBlockStart.gif   二叉堆数据结构,用于OpenList结构 #region 二叉堆数据结构,用于OpenList结构
  2InBlock.gif
  3InBlock.gif    //针对A*算法设计的二叉堆结构,用于OpenList结构
  4InBlock.gif    //KeyID对应PtID,所以唯一
  5InBlock.gif    //Fn可能重复
  6InBlock.gif    //按Fn维护堆结构
  7InBlock.gif    //xc 2006.07.04
  8ExpandedSubBlockStart.gifContractedSubBlock.gif    public Class BinaryHeapItemclass BinaryHeapItem
  9InBlock.gif    {
 10InBlock.gif        public Double Fn;
 11InBlock.gif        public string KeyID;
 12InBlock.gif    }
 13InBlock.gif
 14ExpandedSubBlockStart.gifContractedSubBlock.gif    public Interface iNETA_BinaryHeapMinterface iNETA_BinaryHeapM
 15InBlock.gif    {
 16InBlock.gif        bool AddItem(BinaryHeapItem Item);
 17InBlock.gif        bool MinishItemFnValue(string KeyID, Double NewFn);//A*应用中只会减小Fn
 18InBlock.gif        BinaryHeapItem GetAndRemoveMinFnItem();
 19InBlock.gif        void Clear();
 20InBlock.gif        int ItemCount{get;}
 21InBlock.gif    }
 22InBlock.gif
 23InBlock.gif    //Singleton
 24ExpandedSubBlockStart.gifContractedSubBlock.gif    public Class NETA_BinaryHeapclass NETA_BinaryHeap:iNETA_BinaryHeapM
 25InBlock.gif    {
 26InBlock.gif        public static readonly NETA_BinaryHeap NETA_BinaryHeapInstance = new NETA_BinaryHeap();
 27InBlock.gif        private System.Collections.Hashtable BinaryHeap=new System.Collections.Hashtable();
 28InBlock.gif
 29InBlock.gif        private NETA_BinaryHeap() { }
 30InBlock.gif        
 31InBlock.gif
 32ContractedSubBlock.gifExpandedSubBlockStart.gif        iNETA_BinaryHeapM 成员#region iNETA_BinaryHeapM 成员
 33InBlock.gif
 34InBlock.gif        public bool AddItem(BinaryHeapItem Item)
 35InBlock.gif        {
 36InBlock.gif            try
 37InBlock.gif            {
 38InBlock.gif                if (BinaryHeap.Count == 0)
 39InBlock.gif                    BinaryHeap.Add(1, Item);
 40InBlock.gif                else
 41InBlock.gif                {
 42InBlock.gif                    BinaryHeap.Add(BinaryHeap.Count + 1, Item);
 43InBlock.gif                    int index = BinaryHeap.Count;
 44InBlock.gif                    int temp;
 45InBlock.gif                    double temp1;
 46InBlock.gif                    BinaryHeapItem tempItem;
 47InBlock.gif                    bool isOK = false;
 48InBlock.gif                    while (!isOK)
 49InBlock.gif                    {
 50InBlock.gif                        temp1=index / 2;
 51InBlock.gif                        temp = (int)System.Math.Floor(temp1);
 52InBlock.gif                        if (temp <= 0) break;
 53InBlock.gif
 54InBlock.gif                        if (((BinaryHeapItem)BinaryHeap[temp]).Fn > ((BinaryHeapItem)BinaryHeap[index]).Fn)
 55InBlock.gif                        {
 56InBlock.gif                            tempItem = (BinaryHeapItem)BinaryHeap[temp];
 57InBlock.gif                            BinaryHeap[temp] = (BinaryHeapItem)BinaryHeap[index];
 58InBlock.gif                            BinaryHeap[index] = tempItem;
 59InBlock.gif                            index = temp;
 60InBlock.gif                        }
 61InBlock.gif                        else
 62InBlock.gif                            isOK = true;
 63InBlock.gif                    }
 64InBlock.gif
 65InBlock.gif                }
 66InBlock.gif                return true;
 67InBlock.gif            }
 68InBlock.gif            catch (Exception e)
 69InBlock.gif            {
 70InBlock.gif                MessageBox.Show("NETA_BinaryHeap.AddItem错误" + e.Message);
 71InBlock.gif                return false;
 72InBlock.gif            }
 73InBlock.gif        }
 74InBlock.gif        
 75InBlock.gif        public BinaryHeapItem GetAndRemoveMinFnItem()
 76InBlock.gif        {
 77InBlock.gif            try
 78InBlock.gif            {
 79InBlock.gif                if (BinaryHeap.ContainsKey(1))
 80InBlock.gif                {
 81InBlock.gif                    BinaryHeapItem ResultItem = (BinaryHeapItem)BinaryHeap[1];
 82InBlock.gif                    BinaryHeapItem ChangeItem = (BinaryHeapItem)BinaryHeap[BinaryHeap.Count];
 83InBlock.gif                    BinaryHeap.Remove(BinaryHeap.Count);
 84InBlock.gif                    if (BinaryHeap.Count == 0return ResultItem;
 85InBlock.gif                    BinaryHeap[1= ChangeItem;
 86InBlock.gif
 87InBlock.gif                    int index = 1;
 88InBlock.gif                    int temp;
 89InBlock.gif                    BinaryHeapItem tempItem;
 90InBlock.gif                    bool isOK = false;
 91InBlock.gif                    while (!isOK)
 92InBlock.gif                    {
 93InBlock.gif                        temp = index * 2;
 94InBlock.gif                        if (temp + 1 <= BinaryHeap.Count)
 95InBlock.gif                        {
 96InBlock.gif                            if ((((BinaryHeapItem)BinaryHeap[index]).Fn > ((BinaryHeapItem)BinaryHeap[temp]).Fn) && (((BinaryHeapItem)BinaryHeap[index]).Fn > ((BinaryHeapItem)BinaryHeap[temp + 1]).Fn))
 97InBlock.gif                            {
 98InBlock.gif                                if (((BinaryHeapItem)BinaryHeap[temp]).Fn <= ((BinaryHeapItem)BinaryHeap[temp + 1]).Fn)
 99InBlock.gif                                {
100InBlock.gif                                    tempItem = (BinaryHeapItem)BinaryHeap[temp];
101InBlock.gif                                    BinaryHeap[temp] = (BinaryHeapItem)BinaryHeap[index];
102InBlock.gif                                    BinaryHeap[index] = tempItem;
103InBlock.gif                                    index = temp;
104InBlock.gif                                }
105InBlock.gif                                else
106InBlock.gif                                {
107InBlock.gif                                    tempItem = (BinaryHeapItem)BinaryHeap[temp + 1];
108InBlock.gif                                    BinaryHeap[temp + 1= (BinaryHeapItem)BinaryHeap[index];
109InBlock.gif                                    BinaryHeap[index] = tempItem;
110InBlock.gif                                    index = temp + 1;
111InBlock.gif                                }
112InBlock.gif                            }
113InBlock.gif                            else if (((BinaryHeapItem)BinaryHeap[index]).Fn > ((BinaryHeapItem)BinaryHeap[temp]).Fn)
114InBlock.gif                            {
115InBlock.gif                                tempItem = (BinaryHeapItem)BinaryHeap[temp];
116InBlock.gif                                BinaryHeap[temp] = (BinaryHeapItem)BinaryHeap[index];
117InBlock.gif                                BinaryHeap[index] = tempItem;
118InBlock.gif                                index = temp;
119InBlock.gif                            }
120InBlock.gif                            else if (((BinaryHeapItem)BinaryHeap[index]).Fn > ((BinaryHeapItem)BinaryHeap[temp + 1]).Fn)
121InBlock.gif                            {
122InBlock.gif                                tempItem = (BinaryHeapItem)BinaryHeap[temp + 1];
123InBlock.gif                                BinaryHeap[temp + 1= (BinaryHeapItem)BinaryHeap[index];
124InBlock.gif                                BinaryHeap[index] = tempItem;
125InBlock.gif                                index = temp + 1;
126InBlock.gif                            }
127InBlock.gif                            else
128InBlock.gif                                isOK = true;
129InBlock.gif                        }
130InBlock.gif                        else if ((temp == BinaryHeap.Count) && (((BinaryHeapItem)BinaryHeap[index]).Fn > ((BinaryHeapItem)BinaryHeap[temp]).Fn))
131InBlock.gif                        {
132InBlock.gif                            tempItem = (BinaryHeapItem)BinaryHeap[temp];
133InBlock.gif                            BinaryHeap[temp] = (BinaryHeapItem)BinaryHeap[index];
134InBlock.gif                            BinaryHeap[index] = tempItem;
135InBlock.gif                            index = temp;
136InBlock.gif                        }
137InBlock.gif                        else
138InBlock.gif                            break;
139InBlock.gif                    }
140InBlock.gif                    return ResultItem;
141InBlock.gif                }
142InBlock.gif                else
143InBlock.gif                    return null;
144InBlock.gif            }
145InBlock.gif            catch (Exception e)
146InBlock.gif            {
147InBlock.gif                MessageBox.Show("NETA_BinaryHeap.GetMinFnItem错误" + e.Message);
148InBlock.gif                return null;
149InBlock.gif            }
150InBlock.gif        }
151InBlock.gif       
152InBlock.gif        public bool MinishItemFnValue(string KeyID, double NewFn)
153InBlock.gif        {
154InBlock.gif            try
155InBlock.gif            {
156InBlock.gif                int index=-1;
157InBlock.gif                int temp;
158InBlock.gif                Double temp1;
159InBlock.gif                BinaryHeapItem tempItem;
160InBlock.gif                bool isOK = false;
161InBlock.gif
162InBlock.gif                foreach (DictionaryEntry myDE in BinaryHeap)
163InBlock.gif                {
164InBlock.gif                    if (((BinaryHeapItem)myDE.Value).KeyID == KeyID)
165InBlock.gif                    {
166InBlock.gif                        //A*应用中只能减小Fn
167InBlock.gif                        if (((BinaryHeapItem)myDE.Value).Fn <= NewFn) return false;//非法Fn
168InBlock.gif                        ((BinaryHeapItem)myDE.Value).Fn = NewFn;
169InBlock.gif                        index = (int)(myDE.Key);
170InBlock.gif                        break;
171InBlock.gif                    }
172InBlock.gif                }
173InBlock.gif                if (index == -1return false;
174InBlock.gif
175InBlock.gif                while (!isOK)
176InBlock.gif                {
177InBlock.gif                    temp1 = index / 2;
178InBlock.gif                    temp = (int)System.Math.Floor(temp1);
179InBlock.gif                    if (temp <= 0) break;
180InBlock.gif
181InBlock.gif                    if (((BinaryHeapItem)BinaryHeap[temp]).Fn > ((BinaryHeapItem)BinaryHeap[index]).Fn)
182InBlock.gif                    {
183InBlock.gif                        tempItem = (BinaryHeapItem)BinaryHeap[temp];
184InBlock.gif                        BinaryHeap[temp] = (BinaryHeapItem)BinaryHeap[index];
185InBlock.gif                        BinaryHeap[index] = tempItem;
186InBlock.gif                        index = temp;
187InBlock.gif                    }
188InBlock.gif                    else
189InBlock.gif                        isOK = true;
190InBlock.gif                }
191InBlock.gif
192InBlock.gif                return true;
193InBlock.gif            }
194InBlock.gif            catch (Exception e)
195InBlock.gif            {
196InBlock.gif                MessageBox.Show("NETA_BinaryHeap.MinishItemFnValue错误" + e.Message);
197InBlock.gif                return false;
198InBlock.gif            }
199InBlock.gif        }  
200InBlock.gif
201InBlock.gif        public int ItemCount
202InBlock.gif        {
203InBlock.gif            get { return BinaryHeap.Count; }
204InBlock.gif        }              
205InBlock.gif
206InBlock.gif        public void Clear()
207InBlock.gif        {
208InBlock.gif            BinaryHeap.Clear();
209InBlock.gif        }
210InBlock.gif
211InBlock.gif        #endregion
212InBlock.gif    }
213InBlock.gif
214InBlock.gif#endregion


有了这个二叉堆结构我们的问题就很好就决了,接下来就是照着前面的算法实现我们的代码。

  1 ContractedBlock.gif ExpandedBlockStart.gif NetShortPathAnalyse #region NetShortPathAnalyse
  2InBlock.gif
  3ExpandedSubBlockStart.gifContractedSubBlock.gif    public Class BLL_NetShortPathAnalyseclass BLL_NetShortPathAnalyse:IBLL_NetShortPathAnalyse.iNetShortPathAnalyse
  4InBlock.gif    {
  5InBlock.gif        private IBLL_NetShortPathAnalyse.NETA_NET TheAnalyseNET;
  6InBlock.gif        private NETA_BinaryHeap NetShortPathAnalyseBinaryHeap =NETA_BinaryHeap.NETA_BinaryHeapInstance;
  7InBlock.gif        private iHnValueComputer ShortPathHnValueComputer;
  8InBlock.gif        
  9ContractedSubBlock.gifExpandedSubBlockStart.gifiNetShortPathAnalyse 成员#region iNetShortPathAnalyse 成员
 10InBlock.gif
 11InBlock.gif        public IBLL_NetShortPathAnalyse.NETA_NET NETA_AnalyseNET
 12InBlock.gif        {
 13InBlock.gif            get
 14InBlock.gif            {
 15InBlock.gif                return TheAnalyseNET;
 16InBlock.gif            }
 17InBlock.gif            set
 18InBlock.gif            {
 19InBlock.gif                if (value != null)
 20InBlock.gif                    TheAnalyseNET = value;
 21InBlock.gif            }        
 22InBlock.gif        }
 23InBlock.gif
 24InBlock.gif        public iHnValueComputer SetHnValueComputer
 25InBlock.gif        {
 26InBlock.gif            set 
 27InBlock.gif            {
 28InBlock.gif                if (value != null)
 29InBlock.gif                    ShortPathHnValueComputer = value;
 30InBlock.gif            }
 31InBlock.gif        }
 32InBlock.gif
 33InBlock.gif        public NETA.IBLL_NetShortPathAnalyse.NETA_Point NetShortPathAnalyse(string StartID, string EndID)
 34InBlock.gif        {
 35InBlock.gif            try
 36InBlock.gif            {
 37InBlock.gif                if (TheAnalyseNET == null)
 38InBlock.gif                {
 39InBlock.gif                    MessageBox.Show("请先初始化网络");
 40InBlock.gif                    return null;
 41InBlock.gif                }
 42InBlock.gif                NETA_Point StartPt = TheAnalyseNET.GetPoint(StartID);
 43InBlock.gif                NETA_Point EndPt = TheAnalyseNET.GetPoint(EndID);
 44InBlock.gif                if (StartPt == null)
 45InBlock.gif                {
 46InBlock.gif                    MessageBox.Show("起点在网络中不存在,请重新选择!");
 47InBlock.gif                    return null;
 48InBlock.gif                }
 49InBlock.gif                if (EndPt == null)
 50InBlock.gif                {
 51InBlock.gif                    MessageBox.Show("终点在网络中不存在,请重新选择!");
 52InBlock.gif                    return null;
 53InBlock.gif                }
 54InBlock.gif                if (StartPt.isEnabled == false || EndPt.isEnabled == false)
 55InBlock.gif                {
 56InBlock.gif                    MessageBox.Show("起点或终点被设置为障碍点,请重新选择!");
 57InBlock.gif                    return null;
 58InBlock.gif                }
 59InBlock.gif
 60InBlock.gif
 61InBlock.gif
 62InBlock.gif                //
 63InBlock.gif                TheAnalyseNET.ResetPointState();
 64InBlock.gif                NetShortPathAnalyseBinaryHeap.Clear();
 65InBlock.gif
 66InBlock.gif
 67InBlock.gif
 68InBlock.gif                StartPt.Fn = 0;
 69InBlock.gif                StartPt.Gn = 0;
 70InBlock.gif                StartPt.AnalyseState = PointAnalyseState.InOpenList;
 71InBlock.gif
 72InBlock.gif                BinaryHeapItem MyItem = new BinaryHeapItem();
 73InBlock.gif                MyItem.Fn = StartPt.Fn;
 74InBlock.gif                MyItem.KeyID = StartPt.PointID;
 75InBlock.gif
 76InBlock.gif                NetShortPathAnalyseBinaryHeap.AddItem(MyItem);
 77InBlock.gif
 78InBlock.gif                NETA_Point CurrPt;
 79InBlock.gif                BinaryHeapItem CurrItem;
 80InBlock.gif                while (NetShortPathAnalyseBinaryHeap.ItemCount > 0)
 81InBlock.gif                {
 82InBlock.gif                    CurrItem = NetShortPathAnalyseBinaryHeap.GetAndRemoveMinFnItem();
 83InBlock.gif                    CurrPt = TheAnalyseNET.GetPoint(CurrItem.KeyID);
 84InBlock.gif                    if (CurrPt.PointID == EndPt.PointID) return CurrPt;
 85InBlock.gif
 86InBlock.gif                    CurrPt.AnalyseState = PointAnalyseState.InCloseList;
 87InBlock.gif                    System.Collections.ArrayList ChildrenPt = TheAnalyseNET.GetChildrenPoints(CurrPt);
 88InBlock.gif                    foreach (NETA_Point ChildPt in ChildrenPt)
 89InBlock.gif                    {
 90InBlock.gif                        if (ChildPt.isEnabled == false || ChildPt.AnalyseState == PointAnalyseState.InCloseList)
 91InBlock.gif                        { }
 92InBlock.gif                        else if (ChildPt.AnalyseState != PointAnalyseState.InOpenList)
 93InBlock.gif                        {
 94InBlock.gif                            ChildPt.AnalyseState = PointAnalyseState.InOpenList;
 95InBlock.gif                            ChildPt.ParentPoint = CurrPt;
 96InBlock.gif                            ChildPt.Gn = CurrPt.Gn + ChildPt.Hn;//Hn起临时储存本次Weight作用
 97InBlock.gif                            ChildPt.Hn = ComputeTheHnValue(ChildPt, EndPt);
 98InBlock.gif                            ChildPt.Fn = ChildPt.Gn + ChildPt.Hn;
 99InBlock.gif                            CurrItem = new BinaryHeapItem();
100InBlock.gif                            CurrItem.KeyID = ChildPt.PointID;
101InBlock.gif                            CurrItem.Fn = ChildPt.Fn;
102InBlock.gif                            NetShortPathAnalyseBinaryHeap.AddItem(CurrItem);
103InBlock.gif                        }
104InBlock.gif                        else if (ChildPt.AnalyseState == PointAnalyseState.InOpenList)
105InBlock.gif                        {
106InBlock.gif                            if (CurrPt.Gn + ChildPt.Hn < ChildPt.Gn)
107InBlock.gif                            {
108InBlock.gif                                ChildPt.ParentPoint = CurrPt;
109InBlock.gif                                ChildPt.Gn = CurrPt.Gn + ChildPt.Hn;
110InBlock.gif                                ChildPt.Hn = ComputeTheHnValue(ChildPt, EndPt);
111InBlock.gif                                ChildPt.Fn = ChildPt.Gn + ChildPt.Hn;
112InBlock.gif                                NetShortPathAnalyseBinaryHeap.MinishItemFnValue(ChildPt.PointID, ChildPt.Fn);
113InBlock.gif                            }
114InBlock.gif                        }
115InBlock.gif                    }
116InBlock.gif                }
117InBlock.gif
118InBlock.gif                //if (NetShortPathAnalyseBinaryHeap.ItemCount == 0)
119InBlock.gif
120InBlock.gif                MessageBox.Show("未找到连通路径!");
121InBlock.gif                return null;
122InBlock.gif            }
123InBlock.gif            catch (Exception e)
124InBlock.gif            {
125InBlock.gif                MessageBox.Show("BLL_NetShortPathAnalyse.NetShortPathAnalyse错误" + e.Message);
126InBlock.gif                return null;
127InBlock.gif            }
128InBlock.gif        }
129InBlock.gif
130InBlock.gif        public double ComputeTheHnValue(NETA.IBLL_NetShortPathAnalyse.NETA_Point currPt, NETA.IBLL_NetShortPathAnalyse.NETA_Point EndPt)
131InBlock.gif        {
132InBlock.gif            try
133InBlock.gif            {
134InBlock.gif                if (ShortPathHnValueComputer != null)
135InBlock.gif                    return ShortPathHnValueComputer.doCompute(currPt, EndPt);
136InBlock.gif                else
137InBlock.gif                    throw new Exception("未设置'试探搜索'计算因子");
138InBlock.gif            }
139InBlock.gif            catch(Exception e)
140InBlock.gif            {
141InBlock.gif                MessageBox.Show("计算因子错误," + e.Message);
142InBlock.gif                return 0;
143InBlock.gif            }
144InBlock.gif        }
145InBlock.gif         
146InBlock.gif#endregion
147InBlock.gif
148InBlock.gif    }
149InBlock.gif    #endregion

 



           接下来说说实际应用扩展的问题,在网络GIS中我们会实现连通性分析、事故关阀分析、影响用户分析等,在前面的基础上都能够实现。连通性分析,我们需要找到所有连通的路径,采取的方法是通过设置障碍结点的方式多次计算出最短、次短、再次短。。。,直到找不到连通路径为止。 事故关阀分析,分为点事故和线事故,最终我们可以将点事故、事故线的起点、终点以及失效阀门点统一成一种对象即点事故来处理。思路为:分析从各事故点到各源点之间找出所有连通路径,列出每条路径中经过的并且靠近事故点的阀门设施,最后检查这些阀门的必关性。影响用户分析实在前面分析出必关阀门的基础上找到所有相关联的用户要素,处理方式取决于你设计的用户模型表现形式。再说下权值,我们这里处理成线要素的长度,实际情况中我们也会与特定因素相关联,比如分析最佳交通路径时,我们可以与一个模拟交通状况的复杂的数学模型相关联,对于判断依据 F = G + H 中 H 的处理也是一样的。可以说最短路径分析是解决网络GIS中很多问题的基础,我们可以在此基础之上加入很多能够使我们的模型更接近现实情况的模型因素,以达到跟实际应用跟适应。 同样,还是附上一张效果图:

 最短路径问题的A*算法实现及应用(原创)_第1张图片

                刚开始写,格式老是调不好。

转载于:https://www.cnblogs.com/iEgrhn/archive/2007/04/04/699511.html

你可能感兴趣的:(最短路径问题的A*算法实现及应用(原创))