其实Bellman-Ford算法比Dijkstra算法更加简单,适用面更广,但是算法效率不如Dijkstra.
Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的。这时候,就需要使用其他的算法来求解最短路径,Bellman-Ford算法就是其中最常用的一个。该算法由美国数学家理查德•贝尔曼(Richard Bellman, 动态规划的提出者)和小莱斯特•福特(Lester Ford)发明。Bellman-Ford算法的流程如下:
void Bellman_Ford( const int numOfVertex, /*节点数目*/ const int startVertex, /*源节点*/ int (map)[][6], /*有向图邻接矩阵*/ int *distance, /*各个节点到达源节点的距离*/ int *prevVertex /*各个节点的前一个节点*/ ) { // vector<bool> isInS; //是否已经在S集合中 //isInS.reserve(0); // isInS.assign(numOfVertex, false); //初始化,所有的节点都不在S集合中 , 分配numOfVertex个字节 //step1 /*初始化distance和prevVertex数组*/ for(int i =0; i < numOfVertex; ++i) { distance[ i ] = map[ startVertex ][ i ]; //源节点到各个节点的距离,其中INT_MAX代表不可达 if(map[ startVertex ][ i ] < INT_MAX) //如果是可达的 prevVertex[ i ] = startVertex; else prevVertex[ i ] = -1; //表示还不知道前一个节点是什么 } prevVertex[ startVertex ] = -1; //源节点无前一个节点 /*开始使用贪心思想循环处理不在S集合中的每一个节点*/ //isInS[startVertex] = true; //开始节点放入S集合中 int currentVertex = startVertex; for (int i = 1; i < numOfVertex; i ++) //这里循环从1开始是因为开始节点已经存放在S中了,还有numOfVertex-1个节点要处理 { //step2 /*在Q中选择u到j的distance最小的一个节点, 如第一步A到C,最后目标到D*/ /* int minDistance = INT_MAX; for(int j = 0; j < numOfVertex; ++j) // { if((isInS[j] == false) && (distance[j] < minDistance))//寻找初始currentVertexA到Q中distance最小的节点 最后为新的currentVertexC { currentVertex = j; minDistance = distance[j]; } } isInS[currentVertex] = true;//将这个节点currentVertex放入S集合中 * / //step3 /*对这个新的currentVertexC做松弛计算,更新distance*/ for(int start=0;start<numOfVertex;start++) { for (int j =0; j < numOfVertex; j ++) { if (j!= startVertex && map[start][j] < INT_MAX) //在Q中,有距离的为c->d,c->e, c->b { int currentdist = distance[ start] + map[ start ][ j ]; if (currentdist < distance[ j ]) //distance[j = ]为开始到j的距离 { distance[ j ] = currentdist; prevVertex[ j ] = start; } } } } } //判断是否存在环 bool isLoopExist = false; for(int start=0;start<numOfVertex&& !isLoopExist;start++) { for (int j =0; j < numOfVertex && !isLoopExist; j ++) { if (j!= startVertex && map[start][j] < INT_MAX) //在Q中,有距离的为c->d,c->e, c->b { int currentdist = distance[ start] + map[ start ][ j ]; if (currentdist < distance[ j ]) //distance[j = ]为开始到j的距离 { isLoopExist = true; } } } } if(!isLoopExist) { cout<<"no loop"<<endl; } }
1.可以使用栈,正如前文所示,还有可以使用递归输出
void printPath(int des) { if(preVertex[des]!=-1) printPath(preVertex[des]); cout<<des<<' '; }
因为所有递归都可以转换为栈的操作
使用队列优先替换Bellman中的start循环,让每一次开始的仅仅是那些最有可能使结果更新的点
void SPFA( const int numOfVertex, /*节点数目*/ const int startVertex, /*源节点*/ int (map)[][6], /*有向图邻接矩阵*/ int *distance, /*各个节点到达源节点的距离*/ int *prevVertex /*各个节点的前一个节点*/ ) { //队列和判断index是否在队列的结构 queue<int> q; bool isInQueue[6]; //step1 /*初始化distance和prevVertex数组*/ for(int i =0; i < numOfVertex; ++i) { isInQueue[i] = false; distance[ i ] = INT_MAX; //这个距离必须初始化 为无穷大,否则加入队列的操作不好判断 if(map[ startVertex ][ i ] < INT_MAX) //如果是可达的 prevVertex[ i ] = startVertex; else prevVertex[ i ] = -1; //表示还不知道前一个节点是什么 } prevVertex[ startVertex ] = -1; //源节点无前一个节点 distance[startVertex]=0; q.push(startVertex); isInQueue[startVertex] = true; for (int i = 1; i < numOfVertex; i ++) //这里循环从1开始是因为开始节点已经存放在S中了,还有numOfVertex-1个节点要处理 { //for(int start=0;start<numOfVertex;start++) Bellma-Ford中的使用所有的都循环,先在只循环那些最有可能达到最小解的 //{ while(!q.empty()) { int start = q.front(); cout<<"start"<<start<<endl; for (int j =0; j < numOfVertex; j ++) { cout<<"start1"<<start<<endl; if (j!= startVertex && map[start][j] < INT_MAX) //在Q中,有距离的为c->d,c->e, c->b { int currentdist = distance[ start] + map[ start ][ j ]; if (currentdist < distance[ j ]) //distance[j = ]为开始到j的距离 { distance[ j ] = currentdist; prevVertex[ j ] = start; if(!isInQueue[j]) { q.push(j); isInQueue[j]= true; } } } } q.pop(); isInQueue[start]= false; } } //判断是否存在环 bool isLoopExist = false; for(int start=0;start<numOfVertex&& !isLoopExist;start++) { for (int j =0; j < numOfVertex && !isLoopExist; j ++) { if (j!= startVertex && map[start][j] < INT_MAX) //在Q中,有距离的为c->d,c->e, c->b { int currentdist = distance[ start] + map[ start ][ j ]; if (currentdist < distance[ j ]) //distance[j = ]为开始到j的距离 { isLoopExist = true; } } } } if(!isLoopExist) { cout<<"no loop"<<endl; } }