https://www.cnblogs.com/zhuweisky/archive/2005/09/29/246677.html
以前空閑的時候用C#實現(xiàn)的路徑規(guī)劃算法,今日貼它出來,看大家有沒有更好的實現(xiàn)方案。關于路徑規(guī)劃(最短路徑)算法的背景知識,大家可以參考《C++算法--圖算法》一書。
該圖算法描述的是這樣的場景:圖由節(jié)點和帶有方向的邊構(gòu)成,每條邊都有相應的權(quán)值,路徑規(guī)劃(最短路徑)算法就是要找出從節(jié)點A到節(jié)點B的累積權(quán)值最小的路徑。
首先,我們可以將“有向邊”抽象為Edge類:
????public?classEdge
{
public?stringStartNodeID?;
public?stringEndNodeID???;
public?double?Weight??????;?//權(quán)值,代價????????????}
節(jié)點則抽象成Node類,一個節(jié)點上掛著以此節(jié)點作為起點的“出邊”表。
public?classNode
{
private?stringiD?;
private?ArrayList?edgeList?;//Edge的集合--出邊表
public?Node(stringid?)
{
this.iD?=id?;
this.edgeList?=?newArrayList()?;
}
property
}
在計算的過程中,我們需要記錄到達每一個節(jié)點權(quán)值最小的路徑,這個抽象可以用PassedPath類來表示:
??? ///?///PassedPath?用于緩存計算過程中的到達某個節(jié)點的權(quán)值最小的路徑
///?????public?classPassedPath
{
private?stringcurNodeID?;
private?bool?????beProcessed?;???//是否已被處理????????private?double?????weight?;????????//累積的權(quán)值????????private?ArrayList?passedIDList?;?//路徑public?PassedPath(stringID)
{
this.curNodeID?=ID?;
this.weight????=?double.MaxValue?;
this.passedIDList?=?newArrayList()?;
this.beProcessed?=?false;
}
#region?propertypublic?boolBeProcessed
{
get
{
return?this.beProcessed?;
}
set
{
this.beProcessed?=value?;
}
}
public?stringCurNodeID
{
get
{
return?this.curNodeID?;
}
}
public?doubleWeight
{
get
{
return?this.weight?;
}
set
{
this.weight?=value?;
}
}
publicArrayList?PassedIDList
{
get
{
return?this.passedIDList?;
}
}
#endregion
}
另外,還需要一個表PlanCourse來記錄規(guī)劃的中間結(jié)果,即它管理了每一個節(jié)點的PassedPath。
///?///PlanCourse?緩存從源節(jié)點到其它任一節(jié)點的最小權(quán)值路徑=》路徑表
///?????public?classPlanCourse
{
privateHashtable?htPassedPath?;
#region?ctorpublic?PlanCourse(ArrayList?nodeList?,stringoriginID)
{
this.htPassedPath?=?newHashtable()?;
Node?originNode
=?null;
foreach(Node?node?innodeList)
{
if(node.ID?==originID)
{
originNode
=node?;
}
else
{
PassedPath?pPath
=?newPassedPath(node.ID)?;
this.htPassedPath.Add(node.ID?,pPath)?;
}
}
if(originNode?==?null)
{
throw?new?Exception("The?origin?node?is?not?exist?!")?;
}
this.InitializeWeight(originNode)?;
}
private?voidInitializeWeight(Node?originNode)
{
if((originNode.EdgeList?==?null)?||(originNode.EdgeList.Count?==?0))
{
return;
}
foreach(Edge?edge?inoriginNode.EdgeList)
{
PassedPath?pPath
=?this[edge.EndNodeID]?;
if(pPath?==?null)
{
continue;
}
pPath.PassedIDList.Add(originNode.ID)?;
pPath.Weight
=edge.Weight?;
}
}
#endregion
public?PassedPath?this[stringnodeID]
{
get
{
return?(PassedPath)this.htPassedPath[nodeID]?;
}
}
}
在所有的基礎構(gòu)建好后,路徑規(guī)劃算法就很容易實施了,該算法主要步驟如下:
(1)用一張表(PlanCourse)記錄源點到任何其它一節(jié)點的最小權(quán)值,初始化這張表時,如果源點能直通某節(jié)點,則權(quán)值設為對應的邊的權(quán),否則設為double.MaxValue。
(2)選取沒有被處理并且當前累積權(quán)值最小的節(jié)點TargetNode,用其邊的可達性來更新到達其它節(jié)點的路徑和權(quán)值(如果其它節(jié)點?? 經(jīng)此節(jié)點后權(quán)值變小則更新,否則不更新),然后標記TargetNode為已處理。
(3)重復(2),直至所有的可達節(jié)點都被處理一遍。
(4)從PlanCourse表中獲取目的點的PassedPath,即為結(jié)果。
下面就來看上述步驟的實現(xiàn),該實現(xiàn)被封裝在RoutePlanner類中:
??? ///?///RoutePlanner?提供圖算法中常用的路徑規(guī)劃功能。
///2005.09.06
///?????public?classRoutePlanner
{
publicRoutePlanner()
{
}
#region?Paln//獲取權(quán)值最小的路徑????????public?RoutePlanResult?Paln(ArrayList?nodeList?,string?originID?,stringdestID)
{
PlanCourse?planCourse
=?newPlanCourse(nodeList?,originID)?;
Node?curNode
=?this.GetMinWeightRudeNode(planCourse?,nodeList?,originID)?;
#region?計算過程while(curNode?!=?null)
{
PassedPath?curPath
=planCourse[curNode.ID]?;
foreach(Edge?edge?incurNode.EdgeList)
{
PassedPath?targetPath
=planCourse[edge.EndNodeID]?;
double?tempWeight?=?curPath.Weight?+edge.Weight?;
if(tempWeight?
{
targetPath.Weight
=tempWeight?;
targetPath.PassedIDList.Clear()?;
for(int?i=0?;i
{
targetPath.PassedIDList.Add(curPath.PassedIDList[i].ToString())?;
}
targetPath.PassedIDList.Add(curNode.ID)?;
}
}
//標志為已處理????????????????planCourse[curNode.ID].BeProcessed?=?true;
//獲取下一個未處理節(jié)點????????????????curNode?=?this.GetMinWeightRudeNode(planCourse?,nodeList?,originID)?;
}
#endregion
//表示規(guī)劃結(jié)束????????????return?this.GetResult(planCourse?,destID)?;
}
#endregion
#region?private?method#region?GetResult//從PlanCourse表中取出目標節(jié)點的PassedPath,這個PassedPath即是規(guī)劃結(jié)果????????private?RoutePlanResult?GetResult(PlanCourse?planCourse?,stringdestID)
{
PassedPath?pPath
=planCourse[destID]??;
if(pPath.Weight?==?int.MaxValue)
{
RoutePlanResult?result1
=?new?RoutePlanResult(null?,int.MaxValue)?;
returnresult1?;
}
string[]?passedNodeIDs?=?new?string[pPath.PassedIDList.Count]?;
for(int?i=0?;i
{
passedNodeIDs[i]
=pPath.PassedIDList[i].ToString()?;
}
RoutePlanResult?result
=?newRoutePlanResult(passedNodeIDs?,pPath.Weight)?;
returnresult?;
}
#endregion
#region?GetMinWeightRudeNode//從PlanCourse取出一個當前累積權(quán)值最小,并且沒有被處理過的節(jié)點????????private?Node?GetMinWeightRudeNode(PlanCourse?planCourse?,ArrayList?nodeList?,stringoriginID)
{
double?weight?=?double.MaxValue?;
Node?destNode
=?null;
foreach(Node?node?innodeList)
{
if(node.ID?==originID)
{
continue;
}
PassedPath?pPath
=planCourse[node.ID]?;
if(pPath.BeProcessed)
{
continue;
}
if(pPath.Weight?
{
weight
=pPath.Weight?;
destNode
=node?;
}
}
returndestNode?;
}
#endregion#endregion
}
?2006.05.22?應眾多朋友要求,下面給出一個簡單示例:
RoutePlanner.Plan 過程詳解:
(1)用一張表(PlanCourse)記錄源點到任何其它一節(jié)點的最小權(quán)值,初始化這張表時,如果源點能直通某節(jié)點,則權(quán)值設為對應的
邊的權(quán),否則設為double.MaxValue
(2)選取沒有被處理并且當前累積權(quán)值最小的節(jié)點TargetNode,用其邊的可達性來更新到達其它節(jié)點的路徑和權(quán)值(如果其它節(jié)點
經(jīng)此節(jié)點后權(quán)值變小則更新,否則不更新),然后標記TargetNode為已處理
(3)重復(2),直至所有的可達節(jié)點都被處理一遍。
(4)從PlanCourse表中獲取目的點的PassedPath,即為結(jié)果。
[STAThread]
static?void?Main(string[]?args)
{
ArrayList?nodeList
=?newArrayList()?;
//*****************?B?Node?*******************????????????Node?aNode??=?new?Node("A")?;
nodeList.Add(aNode)?;
//A?->?B????????????Edge?aEdge1?=?newEdge()?;
aEdge1.StartNodeID
=aNode.ID?;
aEdge1.EndNodeID
=?"B";
aEdge1.Weight
=?10;
aNode.EdgeList.Add(aEdge1)?;
//A?->?C????????????Edge?aEdge2?=?newEdge()?;
aEdge2.StartNodeID
=aNode.ID?;
aEdge2.EndNodeID
=?"C";
aEdge2.Weight
=?20;
aNode.EdgeList.Add(aEdge2)?;
//A?->?E????????????Edge?aEdge3?=?newEdge()?;
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?=?newEdge()?;
bEdge1.StartNodeID
=bNode.ID?;
bEdge1.EndNodeID
=?"C";
bEdge1.Weight
=?5;
bNode.EdgeList.Add(bEdge1)?;
//B?->?E????????????Edge?bEdge2?=?newEdge()?;
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????????=?newEdge()?;
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????????=?newEdge()?;
eEdge1.StartNodeID
=eNode.ID?;
eEdge1.EndNodeID
=?"D";
eEdge1.Weight
=?20;
eNode.EdgeList.Add(eEdge1)?;
RoutePlanner?planner
=?newRoutePlanner()?;
RoutePlanResult?result
=?planner.Paln(nodeList?,"A"?,"D")?;
planner
=?null;
}