《算法导论》第24章——单源最短路径

  虽然写这个博客主要目的是为了给我自己做一个思路记忆录,但是如果你恰好点了进来,那么先对你说一声欢迎。我并不是什么大触,只是一个菜菜的学生,如果您发现了什么错误或者您对于某些地方有更好的意见,非常欢迎您的斧正!

在这里插入图片描述
在这里插入图片描述
最短路径的最优子结构
最短路径的子路径也是最短路径。
负权重的边
权重为负数的边。(我的理解:大概知道这个就可以了)
环路
最短路径既不能包含正的环路,也不能包含负的环路。(正的环路,我觉得正常人想走最短的路,都不会在那里打圈,除非路就是圆的;负环路你多绕几圈,最后权重想变多小变多小,就很没意思。)
最短路径的表示
每个节点保持一个前驱属性v.π,指向它的前一个结点。(这样可以串成一串)
松弛操作
说白了就是就是检查一下绕小路会不会更近。
v.d:表示s到v的最短路径估计
在这里插入图片描述
在这里插入图片描述
接下来书中还有一堆性质,我就放图,我自己也没看,感觉很每意思,就是感觉把一些简单的东西说的很复杂,显得十分高大上。在这里插入图片描述

24.1Bellman-Ford算法(贝尔曼-福特算法)

在这里插入图片描述
在这里插入图片描述
我的思路:
这边为什么要先对点循环,然后再套一个对边的循环松弛呢?因为第一次松弛的时候,部分地方还不是最短路径。这边思路真的不是很好描述:最好的办法就是自己按照它的流程走一遍!

24.2有向无环图中的单源最短路径问题

1、对有向无环图进行拓扑排序
2、再进行Bellman类似操作

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
这一章我模棱两可地看了看,emmm不知道它到底要讲些什么,如果你知道的话,请告诉我,谢谢!那么我就要跳过这部分了。

24.3Dijkstra算法(迪杰斯特拉算法)

Dijkstra算法解决的是带权重的有向图上单源最短路径问题,要求所有边权重大于0 。
我的感觉:它就是一个改进的Prim算法。
我的思路:
1、每个点都有一个mark属性标志着是否已经在路径中
2、将边从小排到大
3、如果点没有全部加入到路径中,从小到大遍历每条边,如果边的起点在路径中,边的终点没在路径中,就把终点的前驱设置为起点,同时终点的mark属性变为true
4、开始重新遍历边
在这里插入图片描述
在这里插入图片描述24.4差分约束和最短路径
24.5最短路径性质的证明
四五两章都非常的理论,我就不看了,真的很讨厌这种看的头疼又看不懂的东西。

以下就是代码部分了(建议粘贴到自己的编辑器中运行)

Bellman.h

#pragma once
#define BELLV 5/*点的数量*/
#define BELLE 10/*边的数量*/

/*点*/
typedef struct BellV
{
	char data;/*数据*/
	int d;/*源结点到该点的最短距离*/
	BellV* π;/*前驱结点*/
}Bellv;
/*边*/
typedef struct
{
	Bellv* start;/*起点*/
	Bellv* end;/*终点*/
	int w;/*权重*/
}Belle;
/*图*/
typedef struct
{
	Bellv* v[BELLV];/*点集*/
	Belle* e[BELLE];/*边集*/
}Bellg;

/*初始化*/
void InitBell(Bellg* &G, Bellv* root);
/*松弛操作*/
void BellRelax(Bellv* u, Bellv* v, int w);
/*Bellman算法*/
bool Bellman(Bellg* &G, Bellv* root);
/*打印路径*/
void BellPrint(Bellv *root, Bellv *end);
/*测试函数*/
void TestBellman();

Bellman.cpp

#include "Bellman.h"
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;

/*初始化*/
void InitBell(Bellg* &G,Bellv* root)
{
	for (int i = 0; i < BELLV; i++)
	{
		G->v[i]->d = 100;/*我们假装这个100就是∞*/
		G->v[i]->π = NULL;/*前驱为空*/
	}
	root->d = 0;/*源结点*/
}
/*松弛操作*/
void BellRelax(Bellv* u, Bellv* v, int w)
{
	if (v->d > u->d + w)
	{
		v->d = u->d + w;
		v->π = u;
	}
}
/*Bellman算法*/
bool Bellman(Bellg* &G,Bellv* root)
{
	InitBell(G, root);
	int i, j;

	for (i = 0; i < BELLV; i++)/*对顶点进行遍历*/
	{
		if (G->v[i] != root)/*如果不是根结点*/
		{
			for (j = 0; j < BELLE; j++)
				BellRelax(G->e[j]->start, G->e[j]->end, G->e[j]->w);
		}
	}

	for (i = 0; i < BELLE; i++)/*检测是否有负回路*/
		if (G->e[i]->end->d > G->e[i]->start->d + G->e[i]->w)
			return false;
	return true;
}
/*打印路径*/
void BellPrint(Bellv *root, Bellv *end)
{
	Bellv* tmp = end;
	if (tmp != root)
	{
		BellPrint(root, tmp->π);
		cout << tmp->data << " ";
	}
}

/*测试函数*/
void TestBellman()
{
	/*图*/
	Bellg* G = new Bellg();
	/*点集*/
	Bellv* s = new Bellv(); s->data = 's';
	Bellv* t = new Bellv(); t->data = 't';
	Bellv* x = new Bellv(); x->data = 'x';
	Bellv* y = new Bellv(); y->data = 'y';
	Bellv* z = new Bellv(); z->data = 'z';

	G->v[0] = s; G->v[1] = t;
	G->v[2] = x; G->v[3] = y; G->v[4] = z;

	int i;

	for (i = 0; i < BELLE; i++)
		G->e[i] = new Belle();
	G->e[0]->start = s; G->e[0]->end = t; G->e[0]->w = 6;
	G->e[1]->start = s; G->e[1]->end = y; G->e[1]->w = 7;
	G->e[2]->start = t; G->e[2]->end = x; G->e[2]->w = 5;
	G->e[3]->start = t; G->e[3]->end = y; G->e[3]->w = 8;
	G->e[4]->start = t; G->e[4]->end = z; G->e[4]->w = -4;
	G->e[5]->start = x; G->e[5]->end = t; G->e[5]->w = -2;
	G->e[6]->start = y; G->e[6]->end = x; G->e[6]->w = -3;
	G->e[7]->start = y; G->e[7]->end = z; G->e[7]->w = 9;
	G->e[8]->start = z; G->e[8]->end = s; G->e[8]->w = 2;
	G->e[9]->start = z; G->e[9]->end = x; G->e[9]->w = 7;

	Bellv* root = s;

	if (Bellman(G, root))
	{
		for (i = 0; i < BELLV; i++)
		{
			if (i != 0)
			{
				cout << root->data << " ";
				BellPrint(root, G->v[i]);
				cout << endl;
			}
		}
	}
	else
		cout << "Error!";
}

主函数

#include "Bellman.h"
#include <stdio.h>

int main()
{
	TestBellman();
	getchar();
	getchar();
	return 0;
}

运行结果
在这里插入图片描述
Dijstra.h

#pragma once
#define DIJV 5/*点的数量*/
#define DIJE 10/*边的数量*/

/*点*/
typedef struct dijV
{
	char data;/*数据*/
	dijV* π;/*前驱*/
	bool mark;/*标记是否已经加入到最短路径*/
}dijv;
/*边*/
typedef struct
{
	dijv* start;/*起点*/
	dijv* end;/*终点*/
	int w;/*权重*/
}dije;
/*图*/
typedef struct
{
	dijv* v[DIJV];/*点集*/
	dije* e[DIJE];/*边集*/
}dijg;

/*比较函数*/
bool DijCmp(dije* x, dije* y);
/*Dijkstra算法*/
void Dijkstra(dijg* &g, dijv* root);
/*打印路径*/
void DijPrint(dijv *root, dijv *end);
/*测试函数*/
void TestDijkstra();

Dijstra.cpp

#include "Dijstra.h"
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>/*sort排序函数所需头文件*/
#include<iostream>
using namespace std;


/*比较函数*/
bool DijCmp(dije* x, dije* y)
{
	return x->w < y->w;/*从小到大*/
}
/*Dijkstra算法*/
void Dijkstra(dijg* &g,dijv* root)
{
	int i, j;

	for (i = 0; i < DIJV; i++)
	{
		g->v[i]->mark = false;/*没有被加入最短路径中*/
		g->v[i]->π = NULL;
	}
	root->mark = true;
	sort(g->e, g->e + DIJE, DijCmp);/*按权重排序*/

	j = 0;
	while (j < DIJV - 1)/*还有点没有被选中*/
	{
		for (i = 0; i < DIJE; i++)
		{
			if (g->e[i]->start->mark && !(g->e[i]->end->mark))/*起点在路径中,终点没有*/
			{
				g->e[i]->end->mark = true;
				g->e[i]->end->π = g->e[i]->start;
				j++;
				i = 20;/*结束这个for循环*/
			}
		}
	}
}
/*打印路径*/
void DijPrint(dijv *root, dijv *end)
{
	dijv* tmp = end;
	if (tmp != root)
	{
		DijPrint(root, tmp->π);
		cout << tmp->data << " ";
	}
}
/*测试函数*/
void TestDijkstra()
{
	int i;
	dijg* g = new dijg();/*图*/
	dijv* s = new dijv(); s->data = 's';
	dijv* t = new dijv(); t->data = 't';
	dijv* x = new dijv(); x->data = 'x';
	dijv* y = new dijv(); y->data = 'y';
	dijv* z = new dijv(); z->data = 'z';

	g->v[0] = s; g->v[1] = t;
	g->v[2] = x; g->v[3] = y; g->v[4] = z;

	for (i = 0; i < DIJE; i++)
		g->e[i] = new dije();

	g->e[0]->start = s; g->e[0]->end = t; g->e[0]->w = 10;
	g->e[1]->start = s; g->e[1]->end = y; g->e[1]->w = 5;
	g->e[2]->start = t; g->e[2]->end = x; g->e[2]->w = 1;
	g->e[3]->start = t; g->e[3]->end = y; g->e[3]->w = 2;
	g->e[4]->start = x; g->e[4]->end = z; g->e[4]->w = 4;
	g->e[5]->start = y; g->e[5]->end = t; g->e[5]->w = 3;
	g->e[6]->start = y; g->e[6]->end = x; g->e[6]->w = 9;
	g->e[7]->start = y; g->e[7]->end = z; g->e[7]->w = 2;
	g->e[8]->start = z; g->e[8]->end = s; g->e[8]->w = 7;
	g->e[9]->start = z; g->e[9]->end = x; g->e[9]->w = 6;

	dijv* root = s;
	Dijkstra(g, s);

	for (i = 0; i < DIJV; i++)
	{
		if (i != 0)
		{
			cout << root->data << " ";
			DijPrint(root, g->v[i]);
			cout << endl;
		}
	}
}

主函数

#include "Dijstra.h"
#include <stdio.h>

int main()
{
	TestDijkstra();
	getchar();
	getchar();
	return 0;
}

运行结果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_40851250/article/details/83932707