图的遍历
深度优先搜索
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
int book[101], sum, n, e[101][101];
void dfs(int cur);
int main() {
int m , a, b;
cin >> m >> n;
//读入一个n*n的无向图
//初始化对角线
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] = 9999;
//读入顶点之间的边
for (int i = 1; i <= m; i++) {
cin >> a >> b;
e[a][b] = 1;
e[b][a] = 1;//无向图将,关于对角线对称的两个值都初始化为1
}
book[1] = 1;
dfs(1);
printf("\n");
system("pause");
return 0;
}
void dfs(int cur) {
cout << cur << " ";
sum++;//每访问一个点,就吧记录访问了几个点的sum加1
if (sum == n) return;//所有点都访问完了就递归结束
for (int i = 1; i <= n; i++) {
if (e[cur][i] == 1 && book[i] == 0) { //e[cur][i]就是从cur去访问i
book[i] = 1; //标记过i点已经访问过
dfs(i);
}
}
return;
}
广度优先搜索
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
int m, n, cur, e[505][505], a, b, head, tail, que[10001], book[101] = { 0 }; //注意将book数组都初始化为0,因为book要来判断是否已经访问过
int main() {
cin >> m>> n;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= m; j++)
if (i == j) e[i][j] = 999999999;
else e[i][j] = 0;
}
for (int i = 1; i <= m; i++) {
cin >> a >> b;
e[a][b] = 1;
e[b][a] = 1;
}
head = 1, tail = 1;
que[tail] = 1;//队列中放的是访问过的结点,将que[1]初始化为1的意思是已经访问过第一个点
tail++;
book[1] = 1;
while (head < tail && tail <= n) {
cur = que[head];//cur为当前队列中访问的城市
for (int i = 1; i <= n; i++) {
if (e[cur][i] == 1 && book[i] == 0) { //第一个条件判断两点之间是否有边,第二个条件判断当前访问的下一个点是否被访问过
que[tail] = i;
tail++;
book[i] = 1;//表即访问的点已经被访问
}
if (tail > n) break;
}
head++;//当前点拓展出去的可访问的所有点都访问结束后,才可以对head++进行队列中下一个点的拓展
}
for (int i = 1; i <= n; i++) cout << que[i] << " ";
system("pause");
return 0;
}
深度优先搜索之城市之间最短距离
注意我这里一开始翻了一个严重错误,不能在本城市(点)的拓展中改变dis,否走for的下一次就得到了一个不一样的当前城市(点)dis值。
这里优先使用深度优先搜索,因为所有边(两城市之间的距离)是加权的(距离不一样),广度优先搜索使用于边不加权。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
int m, n, e[101][101], a, b, c,cur, book[101] = { 0 };
int Min = 999999999;
void dfs(int cur, int dis) {
if (dis > Min) return;
if (cur == m)
if (dis < Min) Min = dis;
for(int i=1;i<=m;i++)
//第一个条件判断两城市之间是否有路,第二个判断是否已经访问过
if (e[cur][i] != 999999999 && book[i] == 0) {
book[i] = 1; //将当前点标志位已访问
//dis += e[cur][i];错误!!!不能在本城市(点)的拓展中改变dis,否走for的下一次就得到了一个不一样的当前城市(点)dis值
dfs(i, dis+e[cur][i]);//已当前访问点为起点对其他点进行访问
book[i] = 0;//对当前点访问结束将其标记为未访问
}
return;
}
int main() {
cin >> m >> n;
for(int i=1;i<=m;i++)
for (int j = 1; j <= m; j++)
if (i == j) e[i][j] = 0;//将对角线初始化为0,即一个城市到自己城市标记为0
else e[i][j] = 999999999;
for (int i = 1; i <= n; i++) {
cin >> a >> b >> c;
e[a][b] = c;
//e[b][a] = c;
}
book[1] = 1;
dfs(1, 0);//1表示当前在城市1,0表示当前走过的路径长度为0
cout << "最短路径为:" << Min;
return 0;
}
广度优先搜索之最少转机
用广度优先搜索处理最短距离(不加权)问题时,记录路线所使用的是一个结构体对列。用que[head]来完成拓展功能,que[tail].s来完成记录步骤的功能。注意对que[tail].s的记录过程时,是对que[head].s加1
为什么是que[tail].s =que[head].s + 1???
因为que[head]是每轮对一个head进行点拓展的起始点,这个起始点的拓展点的所有转机次数都应在head这个点上加1(for循环中对当前点进行拓展,所以+!操作在for循环中执行)。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
typedef struct city{
int x;
int s;
}city;
int main(){
city que[250];
int m, n, start, end, e[51][51], a, b, head, tail, book[51] = { 0 };
cin >> m >> n >> start >> end;
//初始化领接矩阵
for (int i = 1; i <= m; i++)
for (int j = 1; j <= m; j++)
if (i == j) e[i][j] = 0; //初始化自己访问自己城市为0
else e[i][j] = 999999999;//将除对角线城市城市均初始化为正无穷
//读入城市之间路线
for (int i = 1; i <= n; i++) {
cin >> a >> b;
e[a][b] = 1; //将之间有航班的城市初始化为1
e[b][a] = 1; //将之间有航班的城市初始化为1
}
//队列初始化
head = 1;
tail = 1;
//从start号城市出发,所以先将start号城市入队 并用tail来入队(不用head),保持与后面操作的一致性
que[tail].x = start;
que[tail].s = 0;
tail++;
book[start] = 1;
int flag = 0;
while (head < tail) {
int cur = que[head].x;//当前访问城市编号
for(int i=1;i<m;i++)
if (e[cur][i] != 999999999 && book[i] == 0) { //可以访问且未被访问
//入队当前城市操作
que[tail].x = i;
que[tail].s =que[head].s + 1;//用tail的s来保存到达要到的终点需要走的步数,即que[tail-1].s
tail++;
book[i] = 1;//标记i城市已经入队(被访问过了)
if (que[tail - 1].x == end) {
flag = 1;
break;
}
}
if (flag == 1) break;//提前结束
head++;//每个点拓展结束后,对队列的头进行++以进行下一次拓展
}
cout << que[tail - 1].s << endl;
return 0;
}
总结:
1.深度优先搜索
深度优先搜索所采用的是递归的拓展方法。深度优先搜索所解决的问题一般是对点的边加权的求最短最长距离问题,在递归过程中做最值的记录更新条件判断即可实现。
广度优先搜索
广度优先搜索采用的是将对一个基准点拓展的所有点放入队列的处理方式。广度优先搜索只是在while循环中使用了for循环来实现。
两种方法都使用了book数组来标记某个点是否访问过。递归是会先标记,后解除标记。