データ構造/グラフ理論:最小パス問題

データ構造/グラフ理論:最小パス問題

1.問題の説明と分類

最短経路問題(短経路問題)は、ネットワークの各エッジに数値(重み)値(長さ、コスト、時間など)がある場合、2つのノード間の合計重みと最小経路を見つけることは、最短経路の問題。
●最短経路問題の分類:との最短経路問題
既知のグローバル情報を最短経路問題
既知のローカル情報
●最短経路問題の分類:
最短パス
重み付けされていないグラフは、重み付きグラフの最短経路が
含ま加重グラフネガティブエッジとを最短経路
:最短経路問題の分類に
単一始点最短経路
グローバル最短パス
二点最短経路

2、アルゴリズムの実装

1.ダイクストラアルゴリズム

解決する問題のタイプ:単一ソースの最短パスの問題
アルゴリズムのアイデア:最短パスのサブパスは依然として最短パスです。最短経路を見つけるために、アルゴリズムは最短経路の長さを増やし、連続的に最短経路を生成します。
(1)最短経路長の最短経路の特性:
この経路には1つの弧のみが含まれている必要があり、この弧の重みは最小です。
(2)次に最短のパス長を持つ最短パスの特性
:ソースポイントからポイントへ(アークが1つだけ)、またはソースポイントから頂点v1を経由して到着するまでの2つの状況のみが可能です。頂点(2つの円弧で構成される)。
(3)次に短い経路長の最短経路の特性:
3つの状況が考えられます:直接ソースポイントからポイント(1つのアークのみ)、またはソースポイントから頂点V1を経由してポイントに到達頂点(2つの円弧で構成される)、またはソースポイントから頂点v2を経由して頂点まで。
(4)残りの最短パスの特性:
ソースポイントからポイント(アークが1つだけ)への直接、またはソースポイントから計算された最短パスの頂点を通る。再び頂点に到達します。

擬似コードの実装:

// Compute shortest path distances from s,
// return them in D
void Dijkstra(Graph* G,int* D,int s)
{
    
    
	//对图G求从s出发到其他顶点的最短距离
	int i,v,w;
	for(i=0;i<G->n();i++)
	{
    
     // Do vertices
	v=minVertex(G,D);// 取目前未访问的距离最短的顶点
	if(D[v]==INFINITY) return;//不可达顶点
	G->setMark(v,VISITED);//加入S集合
	for (w=G->first(v);w<G->n();w= G->next(v,w))
	if(D[w]>(D[v]+G->weight(v,w)))
	D[w]=D[v]+G->weight(v,w);//若从源至W的路径经过V的距离更短,则更新最短距离
	}
}

minVertex関数:

// Find min cost vertex寻找未访问的最短距离顶点
int minVertex(Graph* G,int* D)
{
    
    
	int i,V;
	// Set v to an unvisited vertex
	for(i=0;i<G->n();i++)
	if(G->getMark(i)==UNVISITED)
	{
    
    v=i;break;}
	// Now find smallest D va lue
	for(i++;i<G->n0;i++)
	if((G->getMark(i)==UNVISITED)&&(i<D[v1]))
	v=i;
	return V;
}

優先キューによって実装されるアルゴリズム:

void Dijkstra(Graph* G, int* D, ints)
{
    
    
	int i, v, w;
	// v is current vertex
	DijkElem temp;
	DijkElem E[G->e0]; // Heap array
	temp.distance=0;
	temp.vertex=s;
	E[0]=temp; // Initialize heap array
	minheap<DijkElem,DDComp> H(E,1,G->e0);
	for(i=0;i<G->n();i++)
	{
    
    // Get distances
	do
		{
    
    if(H.size()==0) return;//无数据可删除
		temp=H.removefirst();//取出第一一个元素,即最短距离
		V=temp.vertex;
		} while(G->getMark(v)==VISITED);
	G->setMark(v,VISITED);
	if(D[v]==INFINITY) return;//不可达顶点
	for(w=G->first(V);w<G->n();w=G->next(v,w))
	if (D[w]>(D[v]+G->weight(v, w))
	{
    
    // 更新D
	D[w]=D[v]+G->weight(v,w);
	temp.distance=D[w];
	temp.vertex=w;
	H.insert(temp); // Insert in heap
	}
}

注:Djkstraアルゴリズムは、負でない重みを持つグラフにのみ適しています。

2. Bellman-Fordアルゴリズム

解決する問題のタイプ:単一ソースの最短経路問題
アルゴリズムのアイデア:最短経路長配列シーケンスdist1 [u]、dist2 [u]、...、dist n-1 [u]を作成します。このうち、dist1 [u]は、ソースポイントvから終点uまでの1つのエッジのみを通る最短経路の長さです。dist1[u] = Edge [v] [u];エッジから終点u、dist3 [u]までの最短経路の長さは、ソースポイントvから最大3つのエッジを通って、負の長さのエッジループを形成しない最短経路の長さであり、終点u、...、dist n-1 [u ]は、ソースポイントvから始まり、最大でn-1エッジを経由して、エンドポイントuまで負の長さのサイドループを形成しない最短パスの長さです。
distk1 [i]、j = 0、1、... n-1が計算されたとすると、これは、ソースポイントvからk-1エッジを経由して、負の長さのサイドループを形成せずにエンドポイントjまでの最短パスの長さです。
グラフの隣接行列から、各頂点jから頂点uまでの距離Edge [j] [u]を見つけ、min {dist k-1 [j] + Edge [j] [u]}を計算できます。ソースポイントvから取得できます。各頂点をバイパスして、最大で負の長さのエッジループを構成しないk個のエッジを介して終点uへの最短経路の長さ。
これを使用してdist k-1 [u]と比較し、小さい方をdist k [u]の値として使用します。

擬似コードの実装:

void Graph::BellmanFord ( const int n, const int v )
{
    
    
	//在带权有向图中有的边具有负的权值。从顶点V找到所有其它顶点的最短路径
	for( int i=0;i<n;i++ )
	{
    
    dist[i]=Edge[v][i];
	if(i!=v&&dist[i]<MAXINT)
	path[i]=V;
	else path[i]=-1;
	}
	for(int k=2;k<n;k++)
	for(int u=0;u<n;u++)
	if(u!=v)
		for(i=0;i<n;i++ )
			if(Edge[i][u]>0 && Edge[i][u]<MAXINT && dist[u]>dist[i]+Edge[i][u])
			{
    
    dist[u]=dist[i]+Edge[i][u];
			path[u]=i;
			}
}

注:Bellman-Fordアルゴリズムは、負の重みを持つグラフに適しています。

3.フロイドアルゴリズム

解決する問題のタイプ:すべてのソースの最短経路問題
アルゴリズムのアイデア:
(1)k経路を頂点vからuへの任意の経路として定義し、中間の頂点番号はk未満です。0-pathはvからuへの直接のエッジです。
(2)Dk(v、u)を
vからu への最小のkパスの長さとして定義します(3)vからuへの最短のkパスパスがわかっているとすると、最短の(k + 1)パス頂点kの通過または非通過:頂点kを通過する
場合、最短(k + 1)パスの一部はvからkへの最短kパスであり、他の部分はkからuへの最短kパスです。
それ以外の場合は、その値を変更せずに最短のkパスとして保持します。

擬似コードの実装:

//Floyd's all-pairs shortest paths algorithm
void Floyd(Graph* G)
{
    
    
	int D[G->n()][G->n()]; // Store distances
	for(int i=0;i<G->n();i++) // Initialize
	for(int j=0;j<G->n();j++)
	D[i][j]=G->weight(i,j); // Compute all k paths
	for(int k=0;k<G->n();k++)
	for(int i=0;i<G->n();i++)
	for(int j=0;j<G->n();j++)
	if(D[i][j]>(D[i][k]+D[k][j]))
	D[i][j]=D[i][k]+D[k][j];
}

注:フロイドアルゴリズムは、負の重みを持つグラフにも適しています。

おすすめ

転載: blog.csdn.net/hyl1181/article/details/107780194