图的遍历
深度优先遍历(DFS)
深度优先搜索,从初始节点出发,按预定的顺序扩展到下一个节点,然后从下一节点出发继续扩展新的节点,不断递归执行这个过程,直到某个节点不能再扩展下一个节点为止。
举例题目:n-皇后问题
#include<stdio.h>
#include<string.h>
#include<stdbool.h>
#define N 20
int n;
char g[N][N];
bool col[N], dg[N], udg[N];//col[N]用于判断这一列是否存在Q dg[N和udg[N]用于判断对角线是否存在Q
void dfs(int u) {
int i;
if(u == n) {
for(i = 0; i < n; i++) {
printf("%s", g[i]);
printf("\n");
}
printf("\n");
return;
}
for(i = 0; i < n; i++) {
if(!col[i] && !dg[u + i] && !udg[n - u + i]) {//对角线参照二维坐标系的函数 y = x + b和 y = -x + b
g[u][i] = 'Q';
col[i] = dg[u + i] = udg[n - u + i] = true;
dfs(u + 1);
col[i] = dg[u + i] = udg[n - u + i] = false;
g[u][i] = '.';
}
}
}
int main() {
scanf("%d", &n);
int i, j;
for(i = 0; i < n; i++) {
for(j = 0; j < n; j++) {
g[i][j] = '.';
}
}
dfs(0);
return 0;
}
广度优先遍历(BFS)
举例题目:走迷宫
-
一层一层往外搜索
-
框架:
- 把初始值放入队列
- while循环队列 每次把队头拿出来 拓展队头
-
代码:
#include<stdio.h>
#include<string.h>
#define N 110
typedef struct PII {//数组模拟队列
int x;
int y;
}PII;
PII q[N * N];
int head, tail;
int n, m;
int g[N][N], s[N][N];
int bfs() {
memset(s, -1, sizeof(s));
s[1][1] = 0;//表示该点已经走过了
q[++tail].x = 1;//记录走到的点x坐标
q[tail].y = 1;//记录走到的点y坐标
int vx[4] = {-1, 0, 1, 0}, vy[4] = {0, 1, 0, -1};//上下左右 (x, y)
while(head <= tail) {//队列不为空
PII h = q[head++];//每次取出队头元素 尝试向上下左右扩展
int i;
for(i = 0; i < 4; i++) {
int x = h.x + vx[i], y = h.y + vy[i];//表示走到的点的坐标
if(x >= 1 && x <= n && y >= 1 && y <= m && g[x][y] == 0 && s[x][y] == -1) {//点满足走的条件且在边界内
s[x][y] = s[h.x][h.y] + 1;
q[++tail].x = x, q[tail].y = y;//tail只用加一次
}
}
}
return s[n][m];
}
int main() {
scanf("%d%d", &n, &m);
int i,j;
for(i = 1; i <= n; i++) {
for(j = 1; j <= m; j++) {
scanf("%d", &g[i][j]);//遍历输入每一个点是否可以通过 0表示可以通过 1不可以通过
}
}
printf("%d", bfs());
return 0;
}
图的应用
Dijkstra求最短路
1.初始化距离:只有起点到起点的距离是0,其余都为+∞(未确定)
2.接下来n次迭代(循环) (s:当前已确定的最短距离的点,最开始是原点)
- 找到不在s中的距离最近的点为t
- 把t加到s中
- 用t更新其他点的距离(从t出去的所有边,dis[x] >? dis[t])
-
时间复杂度:O(n^2)
-
稠密图用邻接矩阵 稀疏图用邻接表 这是稠密图
-
没有有向图无向图的区别,无向图是一种特殊的有向图
3.代码:
#include<stdio.h>
#include<string.h>
#define N 510
int g[N][N];//表示i->j的长度
int d[N];//表示1号到i号的当前最短距离
char st[N];//表示i号点的最短距离是否被确定
int n;
int dijkstra() {
memset(d, 0x3f, sizeof d);
d[1] = 0;
int i, j;
for(i = 1; i < n; i++) {
int t = 0;
for(j = 1; j <= n; j++) {
if(!st[j] && (!t || d[j] < d[t])) {//找打一个未被确定最短路径的点 并且该点距离最短 我终于知道啦!st[j] == 0的时候,!st[j]就是1,一个反转的意思
t = j;
}
}
st[t] = 1;//确定该点为最短路径的点
for(j = 1; j <= n; j++) {//找以t为起点的连接的各个点的最短路径
if(d[t] + g[t][j] < d[j]) {
d[j] = d[t] + g[t][j];
}
}
}
if(d[n] == 0x3f3f3f3f) {//0x3f3f3f3f表示无穷大
return -1;
} else {
return d[n];
}
}
int main() {
memset(g, 0x3f, sizeof g);//g表示要填充的内存块 0x3f(作为无穷大使用)表示要被设置的值 第二个g表示要被设置该值的字符数
int m;
scanf("%d%d", &n, &m);//n表示点的个数 m表示边的个数
while(m--) {
int a, b, w;
scanf("%d%d%d", &a, &b, &w);//a b表示点 w表示权重
if(w < g[a][b]) {
g[a][b] = w;//连线上的值的初始化
}
}
printf("%d", dijkstra());
return 0;
}
Prim
-
把所有距离初始化为正无穷
-
进行n次迭代 (s:当前已经在连通块的点)
- 找到不在集合当中的距离最近的点t(最开始随便一个,接下来是找距离最小的点)
- 用t更新其他点到集合的距离
- st[t] = true
-
代码:
#include<stdio.h>
#include<string.h>
#include<stdbool.h>
#define min(a, b) a < b ? a : b
#define N 510
#define INF 0x3f3f3f3f
int n, m;
int g[N][N];
int d[N];
bool st[N];
int prim() {
memset(d, 0x3f, sizeof d);
int i, j;
int res = 0;
for(i = 0; i < n; i++) {
int t = -1;
for(j = 1; j <= n; j++) {
if(!st[j] && (t == -1 || d[t] > d[j])) {//!st[j]用于判断这个点是否被使用 后面的语句用于找从t开始的距离它最小的点
t = j;
}
}
if(i && d[t] == INF) {//通过前面的判断 如果仍然为INF 说明后面没有路径与之相连
return INF;
}
if(i) {//if(i)等价于if(i != 0) 用于排除第一个点
res += d[t];
}
st[t] = true;
for(j = 1; j <= n; j++) {
d[j] = min(d[j], g[t][j]);//找出下一次迭代中以t为起点,各个点到他的距离
}
}
return res;
}
int main() {
scanf("%d%d", &n, &m);
memset(g, 0x3f, sizeof g);
while(m--) {
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
g[a][b] = g[b][a] = min(g[a][b], w);
}
int t = prim();
if(t == INF) {
printf("impossible");
} else {
printf("%d\n", t);
}
return 0;
}
拓扑序列
-
针对有向图,无向图没有拓扑序列
-
若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列
(从前指向后
-
有环不存在拓扑序列
-
有向无环图(拓扑图)一定存在一个拓扑序列 一定至少存在一个入度为0的点
-
入度为0的点:没有任何一条边指向它—>排在当前最前面的位置
-
步骤:
-
把所有入度为0的点入队
-
宽搜:while(队列不空){
取出队头
枚举t的所有出边t->j
删掉t->j j的入度减一 d[j]–
if(d[j] == 0) 让j入队
}
-
(持续更新)
心得体会:图的应用非常多,只要熟悉了计算思路,就可以搞清楚怎么实现这个功能,但是就是写不出来,说明其实最考验的,还是编程能力,因此还是要多敲代码。