图论(二)
实际上按顺序应该先讲深搜和广搜,不过深搜广搜应该都掌握的还可以:这里就用两句经验之谈来进行总结:
深搜:递归遍历当前连通,且未被遍历的结点。一个函数遍历一个节点,到空为止。
广搜:嵌套式遍历当前结点连通的所有未被遍历的结点。一个函数遍历所有结点,每次嵌套遍历到空为止。
本篇将作为考试复习最短路径的第一篇(应该也是最后一篇)文章,主角是能够实现以下功能的算法:
对于邻接矩阵图:初始设无穷为0XFFFFFF的情况下,寻找起点到终点的最短路径长度与最短路径。
没错,Dijkstra算法。
//核心,dijkstra.
void dijstra(int v0) {
memset(dist, 0xffffff, n);
int count = 1;
for (int i = 1; i <= n; i++) {
if (ljb[v0][i] != 0xffffff) {
dist[i] = ljb[v0][i];
}
else {
dist[i] = 0xffffff;
}
}//起点初始化。
for (int i = 1; i <= n; i++) {
solved[i] = false;
path[i] = -1;
} //solve初始化。
solved[v0] = true; //起点已经解决。
for (int i = 1; i <= n; i++) { //进行n次。
int mind = 0xffffff; //找最小距离
int j = 0;
int p = 0;
for (j = 1; j <= n; j++) { //找新赋值的里最小的一个
if (!solved[j] && dist[j] < mind) {
mind = dist[j];
p = j;
}
}
if (count == 1) {
path[j - 1] = v0;
count++;
}
solved[p] = true; //找到则完成任务,以j为起点开始。
if (p == 0) {
return;
}
int w = firstadj(p); //修改直接后继点
while (w != 0) {
if (dist[w] > dist[p] + ljb[p][w]) {//小了就改。
dist[w] = dist[p] + ljb[p][w];
path[w] = p;
}
w = nextadj(p, w);
}//执行完成后,结束。
}
}
//路径
void print_path() {
cout << "遍历起点是?" << endl;
int v0 = 0;
cin >> v0;
cout << "遍历终点是?" << endl;
int ed = 0;
cin >> ed;
dijstra(v0);
cout << dist[ed] << endl;
int p = ed;
while (p != v0 && p > 0) {
cout << p << "->";
p = path[p];
}
cout <<v0 << endl;
}
//邻接矩阵建图
graph() {
for (int i = 0; i < 100; i++) {
for (int j = 0; j <= 100; j++) {
ljb[i][j] = 0xffffff;
}
}
cout << "请输入图的边数n:" << endl;
cin >> n;
memset(visited, false, n + 1);
cout << "请输入对应的边,及对应权值:" << endl;
for (int i = 1; i <= n; i++) {
int m = 0, n1 = 0, v = 0;
cin >> m >> n1 >> v;
ljb[m][n1] = v;
ljb[n1][m] = v;
}
}
首先,我想说明的是,Dijkstra算法是算连通图两点之间最短路径的,确定的是起点,到各个点的长度,而非任意两点,本质上是一种高级贪心(区别于永远选当前最短的那种低级贪心。)其中利用了广度优先搜索,遍历图的路径,每遍历一次,进行一次筛选,确定出最短路的一个顶点。
那么,接下来是重中之重:Dijkstra算法如何理解,记忆?其实分为四个点:
- 函数组成,哪些变量?
- 功能实现,哪些模块?
- 具体记忆,如何进行初始化?
- 具体记忆,如何对数据进行修改?
首先,Dijkstra算法由一个Visted数组(在本篇给的案例中为Solved数组)一个Dist数组,一个图,结点数变量,总起点变量,循环起点变量,中间变量,求邻接点的两个函数,最小变量这些“零件”组成。
模块有初始化数组模块,赋值Dist模块。Dist模块中又分为寻求当前起点模块,广搜模块。而此模块重要的特征是本身基于一个n重循环。
初始化,对于用邻接矩阵表示图的情况来讲,不连通点的初值必须为无限大:0XFFFFFF,否则,会出现混乱,同样,在寻求当前起点模块,最小值初值最好也与邻接表中不连通的点一致。至于Visted数组,自然除起点外全设为false。
最后对数据进行修改,主要记忆,在广搜模块,当存在邻接点时,一直持续筛选,将邻接点的现有距离(该点Dist数组中数据)与当前确定最小距离(本轮起点的Dist)加上邻接路径长度作比,取小的对Dist数组进行修改。选取当前起点时,则注意,应选择未被访问且当前Dist距离最短的点。
本算法对于路径的存储,选择了拉火车式存储,主要利用了数组的性质a[1]=2,a[2]=3,可以用:<1,<2>,3>来表示,意为先走1结点,再来2结点,最后再到3结点。