一、图的一些概念
简单来说,图是由顶点和连接这些顶点的边构成的集合。遍历就是指把图的每一个顶点都访问一次,用一个数表示各个顶点被第几个访问到,这个数就叫时间戳。
图的邻接矩阵存储法:
关联于同一条边的两个结点称为邻接点。关联于同一个结点的两条边称为邻接边。
用一个二维数组储存一个图,第i行第j列表示的就是顶点i到顶点j是否有边(i,j>0),1表示有边,∞表示无边,而自己到自己(i==j时)设为0,若这个二维数组是沿主对角线对称,则这个图是无向图,比如1可以到5,5也可以到1。
二、深度优先遍历
主要思想:以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点,往深处探索,直到没有未被访问过的顶点时,回到上个顶点,继续试探访问别的顶点,直到所有都被访问过。
显然,深度优先遍历是沿图的某一个分支遍历到末端,然后回溯,再沿着另一条进行同样的遍历,直到所有顶点都被访问过为止。
#include<iostream> //深度优先遍历
using namespace std;
int book[100], sum, n, e[100][100];
void dfs(int cur)
{
cout << cur << " ";
book[cur] = 1;
sum++;
if (sum == n)
return;
for (int i = 1; i <= n; ++i)
if (e[cur][i] == 1 && book[i] == 0)
dfs(i);
return;
}
int main()
{
int m, a, b;
cin >> m >> n;
//初始化二维矩阵
for (int i = 1; i <= m; ++i)
{
for (int j = 1; j <= n; ++j)
{
if (i == j)
e[i][j] = 0;
else
e[i][j] = 99999999; //假设99999999为正无穷
}
}
for (int i = 1; i <= m; ++i) //读入顶点间的边
{
cin >> a >> b;
e[a][b] = 1;
e[b][a] = 1; //因为是无向图
}
dfs(1);
system("pause");
}
三、广度优先遍历
主要思想:
首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点,然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束。
#include<iostream> //广度优先遍历
#include<queue>
using namespace std;
int book[100], sum, n, e[100][100];
int main()
{
int m, a, b;
queue<int> q;
cin >> m >> n;
//初始化二维矩阵
for (int i = 1; i <= m; ++i)
{
for (int j = 1; j <= n; ++j)
{
if (i == j)
e[i][j] = 0;
else
e[i][j] = 99999999; //假设99999999为正无穷
}
}
for (int i = 1; i <= m; ++i) //读入顶点间的边
{
cin >> a >> b;
e[a][b] = 1;
e[b][a] = 1; //因为是无向图
}
q.push(1);
book[1] = 1;
cout << "1" << " ";
while (!q.empty())
{
for (int i = 1; i <= n; ++i)
{
if (e[q.front()][i] == 1 && book[i] == 0)
{
book[i] = 1;
q.push(i);
cout << i << " ";
}
}
q.pop();
}
system("pause");
}
题目一:城市地图——图的深度优先遍历
#include<iostream>
using namespace std;
int min = 99999999, n, book[100], e[100][100];
void dfs(int cur, int dis)
{
book[cur] = 1;
if (dis > min)
return;
if (cur == n)
{
if (dis < min)
min = dis;
return;
}
for (int i = 1; i <= n; ++i)
{
if (e[cur][i] !=99999999 && book[i] == 0)
{
dfs(i, dis + e[cur][i]);
book[i] = 0; //注意要收回,因为探索最短路径,另一条路可能还要用到该顶点
}
}
}
int main()
{
int m, a, b, c;
cin >> n >> m; //n个城市8条路径
for (int i = 1; i <= n; ++i) //初始化
{
for (int j = 1; j <= n; ++j)
{
if (i == j)
e[i][j] = 0;
else
e[i][j] = 99999999;
}
}
for (int i = 1; i <= m; ++i) //邻接矩阵储存图
{
cin >> a >> b >> c;
e[a][b] = c;
e[b][a] = c;
}
dfs(1, 0);
cout << min << endl;
system("pause");
}
题目二:最少转机——图的广度优先搜索
我先试着用dfs写了一遍,发现要注意:dfs函数参数中不能写++times!会改变times本身,应该写times+1
dfs:
#include<iostream>
using namespace std;
int m, min = 99999999, book[100], e[100][100];
void dfs(int cur, int times)
{
book[cur] = 1;
if (times > min) //>min则没必要进行
return;
if (cur == m)
{
if (times < min)
min = times;
return;
}
for (int i = 1; i <= m; ++i)
{
if (e[cur][i] == 1 && book[i] == 0)
{
dfs(i, times + 1); //注意不要写++times,因为会将times改变 而当要回来尝试其他航线时就不是原来的times了 结果会出错
book[i] = 0;
}
}
return;
}
int main()
{
int n, a, b;
cin >> m >> n; //m为城市个数,n为航线个数
for (int i = 1; i <= m; ++i)
{
for (int j = 1; j <= m; ++j)
{
if (i == j)
e[i][j] = 0;
else
e[i][j] = 99999999;
}
}
for (int i = 0; i != n; ++i)
{
cin >> a >> b;
e[a][b] = 1;
e[b][a] = 1;
}
dfs(1, 0);
cout << min << endl;
system("pause");
}
bfs:
#include<iostream>
#include<queue>
using namespace std;
int m, book[100], e[100][100];
struct note
{
int cur;
int times;
note(int i, int j) :cur(i), times(j) {};
};
int main()
{
queue<note> q;
int n, a, b;
cin >> m >> n; //m为城市个数 n为航线个数
for (int i = 0; i != n; ++i)
{
cin >> a >> b;
e[a][b] = 1;
e[b][a] = 1;
}
q.push({ 1,0 });
book[1] = 1;
while (!q.empty())
{
for (int i = 1; i <= m; ++i)
{
if (e[q.front().cur][i] == 1 && book[i] == 0)
{
book[i] = 1;
q.push({ i,q.front().times + 1 });
}
if (q.back().cur == m)
goto end;
}
q.pop();
}
end:
cout << q.back().times << endl;
system("pause");
}
这道题用bfs比用dfs更快,因为bfs更适用于所有边权值相同的情况。
此处权值是指边上的值,这个值表明了一种代价,如从一个结点到达另外一个结点的路径的长度、花费的时间、付出的费用等。
而此题的边仅表示一次转机,所以所有边权值相同。