目录
DFS深度优先算法
DFS可以理解为一条“路”走到底,没“路”可以走了再回到上一个“路(jie)口(dian)”,找有没有路可以走,直到没有可以遍历的节点,类似于树的先序遍历。
以树为例,我们先选择根节点1作为初始节点,一条路往下走,1-2-4,发现4号节点没有子节点,于是返回2号节点,访问5号节点,发现5号没有子节点,我们返回2号,2号也没有子节点,我们就返回1号,前往3号,以此类推,完成遍历。
最终完成遍历的结果应当是:1-2-4-5-3-6-7
图的DFS与树相似,我们直接上图和结果
我们以2号节点为根节点,则结果为:2-1-5-6-3-4-8-7
下面上DFS的代码:
int visited[MAX_NODES]; // 标记节点是否已经访问过
int graph[MAX_NODES][MAX_NODES]; // 邻接矩阵
int n; // 图的大小
void dfs(int node) {
//无效节点这直接返回,结束递归
if (node < 0 || node >= n) {
printf("Error: Invalid node index %d\n", node);
return;
}
visited[node] = 1;
printf("%d ", node);
for (int i = 0; i < n; i++) {
if (graph[node][i] && !visited[i]) {
dfs(i);
}
}
}
BFS广度优先算法
BFS可以理解为寻找相邻节点并遍历,直到没有可以遍历的节点
以树为例,若从根结点出发,则先遍历根节点的子节点,然后再遍历子节点的所有子节点,直到全部遍历完。
用容易记忆的方法讲即为一层一层往下走,类比宿管查房,从最顶层(假设为n层)的房间开始查,第n层的每个房间查完,查第n-1层的每一间房间,直到查到1层的最后一间房间停止。
以下图为例,访问的顺序为1-2/3-4/5/6/7
接下来看一下图的例子,与树类似,先查找与当前节点相临近的节点,访问他,再寻找相邻节点的相邻节点并访问,直到所有节点都访问完成。
可以类比洪水爆发,若洪水在A点爆发,则先通过连通B点的“空路”到B点,但是不会去没有“空路”连接的C点。
同样我们以下图为例,若我们从节点2出发,则访问顺序为:2-1/6-5/3-4/7-8
原理为:若我们从节点2出发,则与2相邻且连通的节点为1和6,则我们访问1和6,但是由于2不和5、3两个节点直接相连通,就不访问5和3。接下来1和6相邻且连通的节点为5和3,则继续访问5和3。以此类推访问剩下三个节点。
PS:需要注意的是图中可能存在回路,因此实现代码时需要为每一个节点设置标记值,避免访问已经访问过的节点。树中就不存在回路问题,无需担心这类问题。不过在有向图中可能需要多次调用BFS函数,因为可能从A节点到B节点可以走通,但是B节点到A节点无法走通,导致可能出现最终不能一次遍历完的情况。
PSS:采用邻接矩阵和邻接表两种方法产生的遍历序列不一定一样,具有可变性。
下面是BFS的代码实现
int visited[MAX_NODES] = {0}; // 初始化一个辅助数组为0用于表示所有节点都未被访问
int graph[MAX_NODES][MAX_NODES] = {0}; // 初始化邻接矩阵为0,表示没有边
int n; // 图的大小
void bfs(int start_node) {
// 处理无效的起始节点
if (start_node < 0 || start_node >= n) {
printf("Invalid start node\n");
return;
}
int queue[MAX_NODES];
int front = 0, rear = 0;
queue[rear++] = start_node;
visited[start_node] = 1;
while (front < rear) {
int curr_node = queue[front++];
printf("%d ", curr_node);
for (int i = 0; i < n; i++) {
if (graph[curr_node][i] && !visited[i]) {
visited[i] = 1;
queue[rear++] = i;
}
}
}
printf("\n");
}
PS:在强连通图中,从任意节点出发都只需要调用一次BFS或DFS