图的遍历(深度优先搜索DFS、广度优先搜索BFS)

图的遍历是指从图中的某个顶点出发,按照一定的规则访问图中所有顶点,并使每个顶点仅被访问一次。图的遍历包括深度优先搜索(DFS)和广度优先搜索(BFS)两种主要方法,它们在算法设计、路径搜索、网络分析等方面有广泛的应用。

深度优先搜索(DFS):

类似于树的先序遍历,采用递归或栈的方式实现。其基本思想是从一个起始顶点开始,访问一个顶点后,继续访问它的未访问过的邻接顶点,直到所有邻接顶点都被访问过为止,然后回溯到上一个顶点,继续这一过程,直到所有顶点都被访问过。

递归算法的步骤如下:

1. 访问起始顶点,并标记为已访问。

2. 从该顶点出发,依次访问每个未被访问的邻接顶点,重复步骤 1。

3. 若当前顶点的所有邻接顶点都被访问过,则回溯到上一个顶点,继续访问其他未被访问的邻接顶点。

其递归代码示例如下:

c示例

boolean visited[max_vertex_num]; // 访问标志数组

status (*visitfunc)(int v); // visitfunc 是访问函数,对图的每个顶点调用该函数

 

void dfstraverse(graph g, status (*visit)(int v)) {

    visitfunc = visit;

    for (v = 0; v < g.vexnum; ++v) 

        visited[v] = false; // 访问标志数组初始化

    for (v = 0; v < g.vexnum; ++v) 

        if (!visited[v]) 

            dfs(g, v); // 对尚未访问的顶点调用 dfs

}

 

void dfs(graph g, int v) { // 从第 v 个顶点出发递归地深度优先遍历图 g

    visited[v] = true;

    visitfunc(v); // 访问第 v 个顶点

    for (w = firstadjvex(g, v); w >= 0; w = nextadjvex(g, v, w)) // firstadjvex 返回 v 的第一个邻接顶点,若顶点在 g 中没有邻接顶点,则返回空(0)。若 w 是 v 的邻接顶点,nextadjvex 返回 v 的(相对于 w 的)下一个邻接顶点。若 w 是 v 的最后一个邻接点,则返回空(0)。

        if (!visited[w]) 

            dfs(g, w); // 对 v 的尚未访问的邻接顶点 w 调用 dfs

}

 

广度优先搜索(BFS):

类似于树的层次遍历,采用队列的方式实现。其基本思想是从一个起始顶点开始,访问一个顶点后,将其所有未被访问的邻接顶点依次入队,访问完当前顶点后,出队下一个顶点,继续这一过程,直到所有顶点都被访问过为止。

非递归算法的步骤如下:

1. 访问起始顶点,并标记为已访问,将该顶点入队。

2. 当队列不为空时,出队一个顶点,访问它的所有未被访问的邻接顶点,并将这些邻接顶点依次入队。

3. 重复步骤 2,直到队列为空。

c示例

visited[v] = false; 

initqueue(q); // 置空辅助队列 q

for (v = 0; v < g.vexnum; ++v) 

    if (!visited[v]) {

        visited[v] = true;

        visitfunc(v);

        enqueue(q, v); // v 入队列

        while (!queueempty(q)) {

            dequeue(q, u); // 队头元素出队并置为 u

            for (w = firstadjvex(g, u); w >= 0; w = nextadjvex(g, u, w)) 

                if (!visited[w]) { // w 为 u 的尚未访问的邻接顶点

                    visited[w] = true;

                    visitfunc(w);

                    enqueue(q, w);

                }

        }

    }

 

C++实现

图的深度优先遍历(DFS)和广度优先遍历(BFS):

cpp示例

#include <iostream>

#include <vector>

#include <queue>

 

// 邻接表表示的图

class Graph {

    int numVertices;

    std::vector<std::vector<int>> adjacencyList;

 

public:

    Graph(int vertices) : numVertices(vertices), adjacencyList(vertices) {}

 

    // 添加边

    void addEdge(int src, int dest) {

        adjacencyList[src].push_back(dest);

    }

 

    // 深度优先遍历的辅助函数

    void dfsHelper(int vertex, std::vector<bool>& visited) {

        visited[vertex] = true;

        std::cout << vertex << " ";

 

        for (int neighbor : adjacencyList[vertex]) {

            if (!visited[neighbor]) {

                dfsHelper(neighbor, visited);

            }

        }

    }

 

    // 深度优先遍历

    void dfs(int startVertex) {

        std::vector<bool> visited(numVertices, false);

        dfsHelper(startVertex, visited);

    }

 

    // 广度优先遍历

    void bfs(int startVertex) {

        std::vector<bool> visited(numVertices, false);

        std::queue<int> queue;

 

        visited[startVertex] = true;

        queue.push(startVertex);

 

        while (!queue.empty()) {

            int currentVertex = queue.front();

            queue.pop();

            std::cout << currentVertex << " ";

 

            for (int neighbor : adjacencyList[currentVertex]) {

                if (!visited[neighbor]) {

                    visited[neighbor] = true;

                    queue.push(neighbor);

                }

            }

        }

    }

};

 

int main() {

    Graph g(5); // 5 个顶点的图

 

    g.addEdge(0, 1);

    g.addEdge(0, 2);

    g.addEdge(1, 2);

    g.addEdge(2, 0);

    g.addEdge(2, 3);

    g.addEdge(3, 3);

 

    std::cout << "深度优先遍历: ";

    g.dfs(2);

    std::cout << std::endl;

 

    std::cout << "广度优先遍历: ";

    g.bfs(2);

 

    return 0;

}

在上述代码中, Graph 类表示图。 dfsHelper 函数用于深度优先遍历的递归, dfs 函数从指定顶点开始进行深度优先遍历, bfs 函数进行广度优先遍历。

 

图的遍历操作具有复杂性,主要体现在以下几个方面:

1. 图中没有一个“自然”的首结点,任意一个顶点都可作为第一个被访问的结点。

2. 在非连通图中,从一个顶点出发只能访问它所在的连通分量上的所有顶点,还需考虑选取下一个出发点以访问其余的连通分量。

3. 若图中有回路,一个顶点被访问之后,有可能沿回路又回到该顶点。

4. 一个顶点可以和其他多个顶点相连,访问过后存在选取下一个要访问顶点的问题。

图的遍历还有一些具体的分类,例如:

1. 遍历完所有的边而不能有重复,即所谓“一笔画问题”或“欧拉 路径”。

2. 遍历完所有的顶点而没有重复,即所谓“哈密尔顿问题”。

3. 遍历完所有的边而可以有重复,即所谓“中国邮递员问题”。

4. 遍历完所有的顶点而可以重复,即所谓“旅行推销员问题”。

其中,第一和第三类问题已经得到了完满的解决,而第二和第四类问题则只得到了部分解决。第一类问题就是研究所谓的欧拉图的性质,而第二类问题则是研究所谓的哈密尔顿图的性质。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值