dijkstra朴素算法思想:
1: 用临接矩阵来存储,因为是稠密度的,我们算法复杂度就是O(n^2)
(我们在进行存便之间的关系的时候,已经排除掉重边的干扰了,存的都是最小值)
2: 每一次找出我们存活点当中的距离最小值
3: 利用这个最小值去更新所有点的其它点的最小值
4: 更新完成之后,这个点的使命已经完成,将其杀死;
5: 以上的动作重复n-1 次;就可以了
dijkstra推优化的算法思想:
1: 利用邻接表来进行存储,因为是稀密度的,我们的算法复杂度是o (mlogn)
2: 我们将上面第二步步骤设置未利用堆区维护我们的最小值
3: 每次取出来最小值之后,看看这个点是已经被杀死的点,如果是则重新取出来,如果不是就进行第四步
4: 可以先杀死这个点,也可以跟新完成后再杀死。我们利用这个点,去跟新所有和他相连接的点,如果距离变小了,我们就加入队列里面。
5: 这儿可以看出和朴素版本的区别就是两点:一个是取值,另外一个就是只更新和自己有关的点,而不是朴素算法所有边都来一遍。
bellman_ford算法:
1. 这种算法一般用于有次数限制的方法,且可以有负权边。 时间复杂度就是0(mn)
2: 我们存储的方式变为利用一个结构数组保存我们的信息;
3: 假设k为我们的更新次数,那么我们以下的步骤就循环k次
4: 每次遍历一下我们结构数组,每次对dist进行一次松弛操作。
5: 但是一个1/k中,开始一轮遍历,可能出现了在这次遍历中循环更新以后边,所以我们就利用一个last来存上一次的dist
在last的基础上进行更新。
spfa算法:
1: 针对bellman_fork算法,我们其实没有必要访问所有的边,只有已知边长度已经减少了我们才会更新这条边后面的边。
这就有点bfs的感觉了,利用队列来做。
2. 首先把1号点加进去,并把这个点杀死,然后开始队列的取出-------存入操作
3: 首先,取出这个点,并将其复活。知道为啥是这一步吗?对比dij算法,我们有负权边,可以后退,而dij全是正数,没有后退的余地。
4: 然后遍历取出来的点,如果我们的相连的点减少了。我们呢判断是否在队列里面,如果不再就加入,并将其杀死,以便后面取出来
跟新其它和它相邻的点。
5: 所以这儿的st[N]有两种功能,判断否已经在队列里面。(隐藏了一种杀死复活的感觉。)
Floyd算法:
1: 这个算法没有什么好说的,首先明白注意以下几个点就可以了
2: 由于多元 dist就应该是二元数组 初始化问题就必须进行手动进行
3: 其实这就是一个三维的动态规划问题 记住公式:d[i][j]=min(d[i][j],d[i][k]+d[k][j]);ijk都是从1先开始循环到m;m为点数;
dijkstra朴素算法思想
int g[N][N]; // 存储每条边
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定
// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < n - 1; i ++ )
{
int t = -1; // 在还未确定最短路的点中,寻找距离最小的点
//在st==false的点当中找到 dist最小的点 //遍历了n次找到我们的点 //如果用堆来做 那么就是o(1)
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
// 用t更新其他点的距离 ---遍历了m次边 //如果用堆来做,那么就是修改m次,每次longn;
for (int j = 1; j <= n; j ++ )
dist[j] = min(dist[j], dist[t] + g[t][j]);
//做完以上过程 那么这个t就已经可以确定了
st[t] = true;
}
//如果最后n的最短距离还是无穷大 那么我们就是没有最短距离
if (dist[n] == 0x3f3f3f3f) return -1; 因为这儿没有负权路,所用dist[n] 不可能只是稍微减少一点点;
return dist[n];
}
//------------------------end------------------------------------//
来个完整 程序
using namespace std;
const int N = 50;
int g[N][N]; // 存储每条边
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定
// 求1号点到n号点的最短路,如果不存在则返回-1
int n;
int dijkstra()
{
memset(dist, 0x3f, sizeof dist); //将每一个dist置为无限大
dist[1] = 0;
for (int i = 0; i +1< n ; i++) //总共轮询n-1次,第一次是根据0点去更新其他的点,但是到了n点就不用更新了;
{
int t = -1;
for (int j = 1; j <= n; j++) //每次找出还活着的点中的最小点
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
for (int j = 1; j <= n; j++) //然后根据最小点的更新每一个点
dist[j] = min(dist[j], dist[t] + g[t][j]);
st[t] = true; //杀死这个点
}
if (dist[n] == 0x3f3f3f3f) return -1; //因为没有负边,所以可以这样判断;
return dist[n];
}
int main()
{
cin >> n;
memset(g, 0x3f, sizeof g); //每个距离置为无穷大,理解memset的作用,根据每一个字节进行初始化工作
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cout << g[i][j] << ' ';
}
cout << endl;
}
int m = 14; //写下有多少条边数
while (m--) {
int a, b,c;
cin >> a >> b >> c;
g[a][b]=g[b][a] = min(g[a][b], c); //因为是双向边,所以需要两边都存入
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cout << g[i][j] << ' ';
}
cout << endl;
}
cout << dijkstra()<<endl;
for (int i = 1; i <= n; i++) cout << dist[i] << ' '; //输出每一个距离自己看看哈
return 0;
}
dijkstra堆优化版本
//-----------------------dijkstra堆优化版本--------------------------//
可以手写堆 也可以使用优先队列
可以修改我们的值 不能够修改我们的值
typedef pair<int, int> PII;
int n; // 点的数量
int h[N], w[N], e[N], ne[N], idx; // 邻接表存储所有边
int dist[N]; // 存储所有点到1号点的距离
bool st[N]; // 存储每个点的最短距离是否已确定
// 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 1}); // first存储距离,second存储节点编号
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second, distance = t.first;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > distance + w[i])
{
dist[j] = distance + w[i];
heap.push({dist[j], j});
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
//----------------------------end---------------------------------//
来个完整的程序
using namespace std;
const int N = 55; //点数,一般开大一点
const int M = 2 * N; //边数,因为是双向边,所以也整大一点
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定
int e[M], h[N], ne[M], idx,we[M]; //基本的邻接表的数组
typedef pair<int, int> PII;
int n;
void add(int a, int b, int w) { //开始添加边的信息
e[idx] = b, we[idx] = w, ne[idx] = h[a], h[a] = idx++;
}
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0; //开始的点
priority_queue<PII, vector<PII>, greater<PII>> heap; //定义一个小根堆,注意greater的用法
heap.push({ 0,1 });
while (heap.size()) {
auto t = heap.top(); //每次都取出最小的点
heap.pop();
int ver = t.second, distance = t.first;
if (st[ver]) continue; //如果这个点已经被取过了 那么直接跳过
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i]) { //每次只更新与我相关的点
int j = e[i];
if (dist[j] > we[i] + distance) {
dist[j] = we[i] + distance; //更新这些点的距离
heap.push({ dist[j],j }); //并且压到堆里面去,如果它是最小的,肯定是堆的头了
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
else return dist[n];
}
int main()
{
cin >> n;
memset(h, -1, sizeof h);
int m;
cin >> m;
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c); add(b, a, c); //双向边的操作
}
cout << dijkstra() << endl;
for (int i = 1; i <= n; i++) cout << dist[i] << ' '; //输出检查一下
return 0;
}
bellman-ford算法
可以用来求负环 但一般不用这种算法
(记住加入last数组来备份我们的dist)
int n, m; // n表示点数,m表示边数
int dist[N]; // dist[x]存储1到x的最短路距离
struct Edge // 边,a表示出点,b表示入点,w表示边的权重
{
int a, b, w;
}edges[M];
// 求1到n的最短路距离,如果无法从1走到n,则返回-1。
int bellman_ford()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
// 如果第n次迭代仍然会松弛三角不等式,就说明存在一条长度是n+1的最短路径,由抽屉原理,路径中至少存在两个相同的点,说明图中存在负权回路。
for (int i = 0; i < n; i ++ )
{
for (int j = 0; j < m; j ++ )
{
int a = edges[j].a, b = edges[j].b, w = edges[j].w;
if (dist[b] > dist[a] + w)
dist[b] = dist[a] + w;
}
}
if (dist[n] > 0x3f3f3f3f / 2) return -1;
return dist[n];
}
//-------------------------------end-------------------------------//
spfa算法
int n; // 总点数
int h[N], w[N], e[N], ne[N], idx; // 邻接表存储所有边
int dist[N]; // 存储每个点到1号点的最短距离
bool st[N]; // 存储每个点是否在队列中
// 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
int spfa()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
queue<int> q;
q.push(1);
st[1] = true;
while (q.size())
{
auto t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
if (!st[j]) // 如果队列中已存在j,则不需要将j重复插入
{
q.push(j);
st[j] = true;
}
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
floyd算法
void floyd()
{
for (int k = 1; k <= n; k ++ )
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}