路徑規(guī)劃(最短路徑)算法C#實現(xiàn)

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;

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容