数据结构与算法之广度优先算法

广度优先搜索(Breadth First Search,BFS)是一种图形搜索算法,用于遍历或搜索树或图的数据结构。其主要思想是从起点开始,依次遍历距离该节点最近的所有节点,再依次遍历距离该节点次近的所有节点,以此类推,直到找到目标节点或遍历完整个图。

具体实现上,BFS通常使用队列(Queue)数据结构存储待遍历的节点。从起点开始,将其加入队列中。之后,不断从队列中取出队首节点,并依次遍历其所有未被遍历的邻居节点。将这些邻居节点加入队列中,直到遍历到目标节点或队列为空为止。

BFS算法的优点是可以找到最短路径,即从起点到目标节点的最少步数。同时,BFS算法还可以用于解决一些应用问题,例如迷宫问题、社交网络分析等。

在这里插入图片描述



一、C语言之数据结构与算法之广度优先算法源码实现及详解

下面是一个示例C语言代码实现BFS算法:

#include <stdio.h>
#include <stdbool.h>

#define MAX_VERTICES 100  // 最大节点数量

typedef struct {
    int data[MAX_VERTICES];  // 节点数据
    int size;                // 节点数量
} Queue;

typedef struct {
    int vertices[MAX_VERTICES][MAX_VERTICES];  // 图的邻接矩阵
    int n;                                     // 节点数量
} Graph;

// 初始化队列
void initQueue(Queue *q) {
    q->size = 0;
}

// 判断队列是否为空
bool isQueueEmpty(Queue *q) {
    return q->size == 0;
}

// 入队
void enqueue(Queue *q, int data) {
    q->data[q->size++] = data;
}

// 出队
int dequeue(Queue *q) {
    int data = q->data[0];
    q->size--;
    for (int i = 0; i < q->size; i++) {
        q->data[i] = q->data[i + 1];
    }
    return data;
}

// BFS算法
void bfs(int start, Graph *g, bool visited[]) {
    Queue q;
    initQueue(&q);

    visited[start] = true;
    enqueue(&q, start);

    while (!isQueueEmpty(&q)) {
        int currentVertex = dequeue(&q);
        printf("%d ", currentVertex);

        for (int i = 0; i < g->n; i++) {
            if (g->vertices[currentVertex][i] == 1 && !visited[i]) {
                visited[i] = true;
                enqueue(&q, i);
            }
        }
    }
}

int main() {
    Graph g;
    g.n = 6;

    // 初始化邻接矩阵
    for (int i = 0; i < g.n; i++) {
        for (int j = 0; j < g.n; j++) {
            g.vertices[i][j] = 0;
        }
    }

    // 添加边
    g.vertices[0][1] = 1;
    g.vertices[0][2] = 1;
    g.vertices[1][2] = 1;
    g.vertices[2][0] = 1;
    g.vertices[2][3] = 1;
    g.vertices[3][3] = 1;
    g.vertices[4][5] = 1;
    g.vertices[5][4] = 1;

    bool visited[MAX_VERTICES] = { false };

    bfs(2, &g, visited);

    return 0;
}

在此示例代码中:

  • 使用邻接矩阵来表示图,表示节点之间的关系。
  • 使用队列来存储待遍历的节点,需要实现队列的入队和出队操作。
  • 使用 visited 数组来表示每个节点是否已经被遍历,防止重复遍历。

在main()函数中,建立一个简单的图,并使用广度优先搜索算法从节点2开始遍历整个图。

在这里插入图片描述



二、C++语言之数据结构与算法之广度优先算法源码实现及详解

下面是一个示例C++代码实现BFS算法:

#include <iostream>
#include <queue>
#include <vector>
#include <cstring>

using namespace std;

const int MAX_VERTICES = 100; // 最大节点数量

typedef vector<int> vi;
typedef vector<vi> vv;

bool visited[MAX_VERTICES]; // 表示每个节点是否已经被遍历,防止重复遍历

// BFS算法
void bfs(int start, const vv &graph) {
    queue<int> q;

    visited[start] = true;
    q.push(start);

    while (!q.empty()) {
        int currentVertex = q.front();
        q.pop();
        cout << currentVertex << " ";

        for (int i = 0; i < graph[currentVertex].size(); i++) {
            int neighbor = graph[currentVertex][i];
            if (!visited[neighbor]) {
                visited[neighbor] = true;
                q.push(neighbor);
            }
        }
    }
}

int main() {
    vv graph(6); // 建立一个节点数量为6的图

    // 添加边
    graph[0].push_back(1);
    graph[0].push_back(2);
    graph[1].push_back(2);
    graph[2].push_back(0);
    graph[2].push_back(3);
    graph[3].push_back(3);
    graph[4].push_back(5);
    graph[5].push_back(4);

    memset(visited, false, sizeof(visited)); // 初始化visited数组

    bfs(2, graph); // 从节点2开始BFS遍历图

    return 0;
}

在此示例代码中:

  • 使用邻接表来表示图,表示节点之间的关系。
  • 使用队列来存储待遍历的节点,使用STL库中的queue实现,无需手动实现队列的操作。
  • 使用 visited 数组来表示每个节点是否已经被遍历,防止重复遍历。

在main()函数中,建立一个简单的图,并使用广度优先搜索算法从节点2开始遍历整个图。

在这里插入图片描述



三、java语言之数据结构与算法之广度优先算法源码实现及详解

广度优先算法(Breadth First Search,BFS)是一种常用的图形搜索算法,它从图形的起始节点开始,一层层地向外扩展搜索,直到找到目标节点或者整张图的节点都遍历完毕。BFS算法具有以下特点:

  1. 广度优先搜索是一种盲目搜索策略,不考虑各个节点之间的距离和权重,只关注节点之间的连通性。

  2. BFS算法可以用于寻找最短路径,因为它先搜索到的一定是距离起点最近的节点。

  3. BFS算法需要使用队列这种数据结构进行实现。

下面就来介绍一下BFS算法的源码实现及详解:

import java.util.ArrayDeque;
import java.util.Queue;

public class BFS {
    /* 定义节点类 */
    static class Node{
        int x, y;  // 节点的坐标
        Node(int x, int y){
            this.x = x;
            this.y = y;
        }
    }

    public static void main(String[] args) {
        /* 定义迷宫地图 */
        char[][] maze = {
                {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
                {'#', '.', '#', '.', '.', '.', '#', '.', '.', '#'},
                {'#', '.', '#', '#', '.', '.', '#', '.', '#', '#'},
                {'#', '.', '.', '.', '.', '.', '.', '.', '.', '#'},
                {'#', '#', '#', '#', '#', '.', '#', '#', '.', '#'},
                {'#', '.', '.', '.', '.', '.', '#', '#', '#', '#'},
                {'#', '#', '#', '#', '.', '.', '.', '.', '.', '#'},
                {'#', '.', '.', '#', '.', '#', '.', '#', '.', '#'},
                {'#', '.', '.', '.', '#', '.', '#', '.', '.', '#'},
                {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
        };

        /* 定义起点和终点 */
        Node start = new Node(1, 1);
        Node end = new Node(8, 8);

        /* 定义队列 */
        Queue<Node> queue = new ArrayDeque<>();
        queue.add(start);

        /* 定义数组记录每个节点是否被访问过 */
        boolean[][] visited = new boolean[10][10];
        visited[start.x][start.y] = true;

        /* 定义数组记录每个节点的父节点 */
        Node[][] parent = new Node[10][10];
        parent[start.x][start.y] = start;

        /* 开始广度优先搜索 */
        while(!queue.isEmpty()){
            /* 取出队列的头节点 */
            Node cur = queue.poll();
            /* 判断是否到达终点 */
            if(cur.x == end.x && cur.y == end.y) break;

            /* 遍历当前节点的所有邻居节点 */
            int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 定义四个方向
            for(int[] dir : dirs){
                int nextX = cur.x + dir[0];
                int nextY = cur.y + dir[1];
                /* 判断下一个节点是否越界或者已经访问过 */
                if(nextX < 0 || nextX >= maze.length || nextY < 0 || nextY >= maze[0].length || visited[nextX][nextY] || maze[nextX][nextY] == '#') continue;
                /* 添加下一个节点到队列中 */
                queue.add(new Node(nextX, nextY));
                visited[nextX][nextY] = true;
                parent[nextX][nextY] = cur;
            }
        }

        /* 输出最短路径 */
        Node cur = end;
        while(cur != start){
            System.out.print("(" + cur.x + "," + cur.y + ") <- ");
            cur = parent[cur.x][cur.y];
        }
        System.out.println("(" + start.x + "," + start.y + ")");
    }
}

以上代码是一个简单的迷宫求解问题,我们将起点和终点之间的最短路径用BFS算法求出并输出。下面对代码中的主要部分进行详解:

  1. 定义节点类
static class Node{
    int x, y;  // 节点的坐标
    Node(int x, int y){
        this.x = x;
        this.y = y;
    }
}

节点类包含一个x、y坐标,表示节点在迷宫地图中的位置。

  1. 定义队列
Queue<Node> queue = new ArrayDeque<>();
queue.add(start);

BFS算法需要使用队列来进行实现,我们使用Java标准库中的ArrayDeque来实现队列。将起点加入队列中。

  1. 定义数组记录每个节点是否被访问过
boolean[][] visited = new boolean[10][10];
visited[start.x][start.y] = true;

为了避免重复访问节点,我们需要使用一个二维布尔数组visited来记录每个节点是否被访问过。将起点标记为已访问。

  1. 定义数组记录每个节点的父节点
Node[][] parent = new Node[10][10];
parent[start.x][start.y] = start;

在搜索过程中,我们需要记录每个节点的父节点,以便在搜索结束后回溯寻找最短路径。我们使用一个二维Node数组parent来记录每个节点的父节点。将起点的父节点设置为其自身。

  1. 开始广度优先搜索
while(!queue.isEmpty()){
    /* 取出队列的头节点 */
    Node cur = queue.poll();
    /* 判断是否到达终点 */
    if(cur.x == end.x && cur.y == end.y) break;

    /* 遍历当前节点的所有邻居节点 */
    int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 定义四个方向
    for(int[] dir : dirs){
        int nextX = cur.x + dir[0];
        int nextY = cur.y + dir[1];
        /* 判断下一个节点是否越界或者已经访问过 */
        if(nextX < 0 || nextX >= maze.length || nextY < 0 || nextY >= maze[0].length || visited[nextX][nextY] || maze[nextX][nextY] == '#') continue;
        /* 添加下一个节点到队列中 */
        queue.add(new Node(nextX, nextY));
        visited[nextX][nextY] = true;
        parent[nextX][nextY] = cur;
    }
}

从队列中取出一个节点,遍历其所有邻居节点,将没有访问过的节点加入队列中,并标记其父节点。如果找到了终点,则退出循环。

  1. 输出最短路径
Node cur = end;
while(cur != start){
    System.out.print("(" + cur.x + "," + cur.y + ") <- ");
    cur = parent[cur.x][cur.y];
}
System.out.println("(" + start.x + "," + start.y + ")");

通过回溯每个节点的父节点,从终点一直走到起点,输出最短路径。

总结:

BFS算法是一种常用的图形搜索算法,它的实现需要用到队列、数组等数据结构。在使用BFS算法时,需要定义节点类、队列、visited、parent等数组或变量,然后按照广度优先的顺序逐层遍历每个节点,记录每个节点的父节点,最后回溯寻找最短路径。

在这里插入图片描述






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值