不带赋权的图最短路径算法参考前面的最短路径算法-广度优先搜索
带赋权但不含负边的图最短路径算法参考迪克斯特拉算法
这里考虑带赋权且具有负边的最短路径(但不考虑负值圈)。
由于存在负值边,因此可能存在路径v->u->s路径长小于v->s,因此迪克斯特拉算法不再成立。
这里我们需要做个改变,首先将起始节点放入一个列表,取出一个节点v1,对于v1的某个邻接节点v2,记起始节点到v2的距离目前为d2,起始节点到v1的距离目前为d1,v1->v2的距离为d,如果d1+d < d2,则更新v2节点的距离d2,如果v2不在列表中,则将v2放回列表,继续取出下一个节点,直到列表为空。
考虑如下图:
将v5->v7的路径设为-1。因此从v1->v7的最短路径长由v1->v4->v7变为v1->v4->v5->v7,且长度变为2.
实现代码如下:
#include<iostream>
using namespace std;
int main()
{
int input[8][8] = {
{0,1,0,0,0,0,0,0}, //将起始节点放入列表,以此作为循环的开始
{0,0,2,0,1,0,0,0},
{0,0,0,0,3,10,0,0},
{0,4,0,0,0,0,5,0},
{0,0,0,2,0,2,8,4},
{0,0,0,0,0,0,0,-1},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,0}
};
int path[8] = {0,0,999,999,999,999,999,999};//将起始节点距离声明为0,其余节点声明为无穷大
while (true)
{
int i = 0;
while (++i < 8 && input[0][i] == 0){} //取出列表中的一个节点
if (i == 8)
break;//如果列表中没有节点了,则退出循环
input[0][i] = 0;//将节点移出列表
for (int j = 1; j < 8; j++)
{
if (input[i][j] != 0)
{
int tmp = path[i] + input[i][j];
if (tmp < path[j])
{
path[j] = tmp;
input[0][j] = 1;//如果j节点路径信息更新了,则放入列表
}
}
}
}
}