算法系列之搜索算法-广度优先搜索BFS

简介: 广度优先搜索(BFS)是一种非常强大的算法,特别适用于解决最短路径、层次遍历和连通性问题。在面试中,掌握BFS的基本实现和应用场景,能够帮助你高效解决许多与图或树相关的问题。

85112d4b-440b-42c0-afe1-a258d2b5cd86.jpg

随着每年"金三银四"招聘季的到来,许多求职者开始积极备战面试。在众多面试环节中,机试往往是不可或缺的一环,而算法能力更是机试考核的重点。为此,我们特别推出算法系列文章,帮助大家系统复习算法知识。今天,我们将首先探讨搜索算法中的重要内容——广度优先搜索(BFS)。

图的介绍

图(Graph)是一种非线性的数据结构,由顶点(Vertex)和边(Edge)组成。如下图所示

_20250219193211.jpg
分类如下:

  • 无向图(Undirected Graph):边没有方向,表示双向关系。

  • 有向图(Directed Graph):边有方向,表示单向关系。

  • 加权图(Weighted Graph):边带有权重。

  • 无权图(Unweighted Graph):边没有权重。

广度优先搜索(BFS, Breadth-First Search)

BFS(广度优先搜索,Breadth-First Search)是一种用于遍历或搜索图的算法。它从根节点开始,逐层访问(由近及远)所有相邻节点,直到找到目标节点或遍历完整个结构。BFS的核心思想是“先广后深”,即先访问离起点最近的节点,再逐步扩展到更远的节点。

BFS 通常借助于队列来实现。队列具有"先进先出(FIFO)"特性非常适合BFS的逐层遍历需求。其实现步骤如下:

  1. 初始化队列:使用一个队列来存储待访问的节点。
  2. 标记已访问节点:使用一个集合记录已访问的节点,避免重复访问。
  3. 从起点开始:将起点加入队列,并标记为已访问。
  4. 循环处理队列:
    • 从队列中取出一个节点。
    • 检查该节点是否为目标节点(如果有目标节点的条件)。
    • 如果不是目标节点,将其所有未访问的邻接节点加入队列,并标记为已访问。
  5. 重复步骤4,直到队列为空或找到目标节点。

示例代码如下:

public class BFSExample {
   
    // 定义图的节点类
    static class Node {
   
        int value;
        List<Node> neighbors;

        public Node(int value) {
   
            this.value = value;
            this.neighbors = new ArrayList<>();
        }

        // 添加邻接节点
        public void addNeighbor(Node neighbor) {
   
            this.neighbors.add(neighbor);
        }
    }

    //bfs函数
    public static void bfs(Node startNode) {
   
        if(startNode == null ) return;

        // 使用队列存储待访问的节点
        Queue<Node> queue = new LinkedList<>();

        // 使用HashSet记录已访问的节点
        Set<Node> visited = new HashSet<>();

        // 将起点加入队列并标记为已访问
        queue.add(startNode);
        visited.add(startNode);

        // 遍历队列
        while (!queue.isEmpty()) {
   
            // 从队列中取出一个节点
            Node currentNode = queue.poll();
            //打印当前顶点
            System.out.print(currentNode.value + " ");

            // 遍历当前节点的所有邻接节点
            for (Node neighbor : currentNode.neighbors) {
   
                // 如果邻接节点未被访问,则加入队列并标记为已访问
                if (!visited.contains(neighbor)) {
   
                    queue.add(neighbor);
                    visited.add(neighbor);
                }
            }
        }


    }

    public static void main(String[] args) {
   
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);
        Node node6 = new Node(6);


        node1.addNeighbor(node2);
        node1.addNeighbor(node3);
        node1.addNeighbor(node5);

        node2.addNeighbor(node1);
        node2.addNeighbor(node3);
        node2.addNeighbor(node5);

        node3.addNeighbor(node1);
        node3.addNeighbor(node2);
        node3.addNeighbor(node4);
        node3.addNeighbor(node6);

        node4.addNeighbor(node3);
        node4.addNeighbor(node6);

        node5.addNeighbor(node2);
        node5.addNeighbor(node6);

        node6.addNeighbor(node3);
        node6.addNeighbor(node4);
        node6.addNeighbor(node5);

        bfs(node2);
    }

}

BFS的特点

  • 逐层遍历:BFS按层次顺序访问节点,适合求解最短路径问题、层次遍历和连通性问题
  • 空间复杂度较高:需要存储所有待访问的节点,空间复杂度通常为O(V),其中V是节点数量。
  • 时间复杂度:对于图,时间复杂度为O(V + E),其中V是节点数,E是边数。

BFS 示例题

以下列举了一些机试题

BOSS的收入

  • 题目描述:

一个 XX 产品行销总公司,只有一个boss,其有若干一级分销,一级分销又有若干二级分销,每个分销只有唯一的上级分销。规定,每个月,下级分销需要将自己的总收入(自己的+下级上交的)每满 100 元上交 15 元给自己的上级。

现给出一组分销的关系,和每个分销的收入,请找出boss并计算出这个boss的收入。

比如:

收入 100 元,上交 15 元;

收入 199 元(99 元不够 100),上交 15 元,

收入 200 元,上交 30 元。

输入:

分销关系和收入:
【【分销id 上级分销的Id收入】,【分销id 上级分销的id收入】,【分销id 上级分销的id收入】】

分销ID范围 0..65535
收入范围 0..65535,单位元

提示:输入的数据只存在 1 个boss,不存在环路

输出:【boss的ID,总收入】

输入描述
第 1 行输入关系的总数量 N

第 2 行开始,输入关系信息,格式:分销ID 上级分销ID 收入

比如:

5
1 0 100
2 0 199
3 0 200
4 0 200
5 0 200

输出描述
输出:boss的ID 总收入
比如:
0 120

补充说明
给定的输入数据都是合法的,不存在坏路,重复的。

代码如下:

public class BFSBoss {
   
    public static void main(String[] args) {
   
        Scanner scanner = new Scanner(System.in);

        // 读取关系的总数量
        int N = scanner.nextInt();

        // 用于存储每个分销商的上级
        Map<Integer, Integer> parentMap = new HashMap<>();

        // 用于存储每个分销商的收入
        Map<Integer, Integer> incomeMap = new HashMap<>();

        // 构建⼊度哈希表,key是节点名,value是⼊度(其有几个下级分销商)
        Map<Integer, Integer> indegree = new HashMap<>();

        // 读取所有关系
        for (int i = 0; i < N; i++) {
   
            int id = scanner.nextInt();
            int parentId = scanner.nextInt();
            int income = scanner.nextInt();

            parentMap.put(id, parentId);
            incomeMap.put(id, income);

            // 初始化子节点列表
            indegree.put(parentId, indegree.getOrDefault(parentId, 0)+1);
        }

        // 找到boss(没有上级的分销商)
        int bossId = -1;
        for (int id : indegree.keySet()) {
   
            if (!parentMap.containsKey(id)) {
   
                bossId = id;
                break;
            }
        }

        // BFS遍历计算收入
        Queue<Integer> queue = new LinkedList<>();
        for (int id : parentMap.keySet()) {
   
            if (indegree.getOrDefault(id,0) == 0) {
   
                queue.add(id); // 将所有叶子节点加入队列
            }
        }

        while (!queue.isEmpty()) {
   
            int currentId = queue.poll();

            if (currentId == bossId) {
   
                break; // 已经到达boss,无需再向上传递
            }
            int parentId = parentMap.get(currentId);

            // 计算当前分销商的总收入
            int totalIncome = incomeMap.get(currentId);

            // 计算需要上交的收入
            int submitIncome = (totalIncome / 100) * 15;

            // 更新当前分销商上级的收入
            incomeMap.put(parentId, incomeMap.getOrDefault(parentId,0) + submitIncome);
            // 更新上级分销商的⼊度
            indegree.put(parentId, indegree.get(parentId)-1);

            // 上级分销商的下级分销收入上交完成,则将上级分销商加入队列
            if(indegree.get(parentId) == 0){
   
                queue.add(parentId);
            }

        }

        // 输出boss的ID和总收入
        System.out.println(bossId + " " + incomeMap.get(bossId));
    }
}

总结

广度优先搜索(BFS)是一种非常强大的算法,特别适用于解决最短路径、层次遍历和连通性问题。在面试中,掌握BFS的基本实现和应用场景,能够帮助你高效解决许多与图或树相关的问题。希望本文的介绍和例题能够帮助你更好地理解和应用BFS算法。

目录
相关文章
|
5月前
|
存储 算法 调度
基于和声搜索优化算法的机器工作调度matlab仿真,输出甘特图
本程序基于和声搜索优化算法(Harmony Search, HS),实现机器工作调度的MATLAB仿真,输出甘特图展示调度结果。算法通过模拟音乐家即兴演奏寻找最佳和声的过程,优化任务在不同机器上的执行顺序,以最小化完成时间和最大化资源利用率为目标。程序适用于MATLAB 2022A版本,运行后无水印。核心参数包括和声记忆大小(HMS)等,适应度函数用于建模优化目标。附带完整代码与运行结果展示。
152 24
|
5月前
|
人工智能 自然语言处理 算法
阿里云 AI 搜索开放平台:从算法到业务——AI 搜索驱动企业智能化升级
本文介绍了阿里云 AI 搜索开放平台的技术的特点及其在各行业的应用。
605 3
|
2月前
|
存储 搜索推荐 算法
加密算法、排序算法、字符串处理及搜索算法详解
本文涵盖四大类核心技术知识。加密算法部分介绍了对称加密(如 AES)、非对称加密(如 RSA)、哈希摘要(如 SHA-2)、签名算法的特点及密码存储方案(加盐、BCrypt 等)。 排序算法部分分类讲解了比较排序(冒泡、选择、插入、归并、快排、堆排序)和非比较排序(计数、桶、基数排序)的时间复杂度、适用场景及实现思路,强调混合排序的工业应用。 字符串处理部分包括字符串反转的双指针法,及项目中用正则进行表单校验、网页爬取、日志处理的实例。 搜索算法部分详解了二分查找的实现(双指针与中间索引计算)和回溯算法的概念(递归 + 剪枝),以 N 皇后问题为例说明回溯应用。内容全面覆盖算法原理与实践
121 0
|
7月前
|
机器学习/深度学习 算法
算法系列之搜索算法-深度优先搜索DFS
深度优先搜索和广度优先搜索一样,都是对图进行搜索的算法,目的也都是从起点开始搜索,直到到达顶点。深度优先搜索会沿着一条路径不断的往下搜索,直到不能够在继续为止,然后在折返,开始搜索下一条候补路径。
387 62
算法系列之搜索算法-深度优先搜索DFS
|
3月前
|
机器学习/深度学习 算法 数据可视化
基于Qlearning强化学习的机器人迷宫路线搜索算法matlab仿真
本内容展示了基于Q-learning算法的机器人迷宫路径搜索仿真及其实现过程。通过Matlab2022a进行仿真,结果以图形形式呈现,无水印(附图1-4)。算法理论部分介绍了Q-learning的核心概念,包括智能体、环境、状态、动作和奖励,以及Q表的构建与更新方法。具体实现中,将迷宫抽象为二维网格世界,定义起点和终点,利用Q-learning训练机器人找到最优路径。核心程序代码实现了多轮训练、累计奖励值与Q值的可视化,并展示了机器人从起点到终点的路径规划过程。
116 0
|
6月前
|
算法 安全 Java
算法系列之广度优先搜索解决妖怪和尚过河问题
BFS 是一种逐层扩展的搜索算法,适用于寻找最短路径。我们可以将每个状态看作图中的一个节点,合法的移动就是节点之间的边。通过 BFS,我们可以找到从初始状态到目标状态的最短路径。
150 30
算法系列之广度优先搜索解决妖怪和尚过河问题
|
6月前
|
算法 Java
算法系列之深度/广度优先搜索解决水桶分水的最优解及全部解
在算法学习中,广度优先搜索(BFS)适用于解决最短路径问题、状态转换问题等。深度优先搜索(DFS)适合路径搜索等问题。本文将介绍如何利用广度优先搜索解决寻找`3 个 3、5、8 升水桶均分 8 升水`的最优解及深度优先搜索寻找可以解决此问题的所有解决方案。
142 7
 算法系列之深度/广度优先搜索解决水桶分水的最优解及全部解
|
6月前
|
监控 算法 安全
公司电脑网络监控场景下 Python 广度优先搜索算法的深度剖析
在数字化办公时代,公司电脑网络监控至关重要。广度优先搜索(BFS)算法在构建网络拓扑、检测安全威胁和优化资源分配方面发挥重要作用。通过Python代码示例展示其应用流程,助力企业提升网络安全与效率。未来,更多创新算法将融入该领域,保障企业数字化发展。
143 10
|
6月前
|
监控 算法 安全
基于 Python 广度优先搜索算法的监控局域网电脑研究
随着局域网规模扩大,企业对高效监控计算机的需求增加。广度优先搜索(BFS)算法凭借其层次化遍历特性,在Python中可用于实现局域网内的计算机设备信息收集、网络连接状态监测及安全漏洞扫描,确保网络安全与稳定运行。通过合理选择数据结构与算法,BFS显著提升了监控效能,助力企业实现智能化的网络管理。
94 7
|
25天前
|
机器学习/深度学习 算法 新能源
【优化调度】基于matlab粒子群算法求解水火电经济调度优化问题研究(Matlab代码实现)
【优化调度】基于matlab粒子群算法求解水火电经济调度优化问题研究(Matlab代码实现)

热门文章

最新文章