华为OD机试 - 小菊的寻宝之旅 - 广度优先搜索(Java 新系统 100分)

本题为华为OD机试真题,要求在无权无向图中求起点到终点的最短路径长度(边数)。采用广度优先搜索(BFS)算法,利用邻接表建图、队列逐层扩展、距离数组记录最短距离。时间复杂度O(n+m),空间复杂度O(n+m),适用于n≤10000、m≤100000的大规模图。支持自环与重边,不可达时返回-1。

在这里插入图片描述

华为OD机试 新系统 题库疯狂收录中,刷题点这里

专栏导读

本专栏收录于《华为OD机试(JAVA)真题》。

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天优快云在线答疑。

一、题目内容

小菊听说在一个神秘的城堡里藏着宝藏,她决定去寻宝。城堡由 n n n 个房间组成,某些房间之间有通道相连。小菊从房间 s s s 出发,想要到达藏有宝藏的房间 t t t。

给定房间数量 n n n、通道列表 edges、起点 s s s 和终点 t t t,求小菊从起点到终点经过通道最短的路径长度(通道长度相同且长度为 1,例如:房间 1 → 房间 2 → 房间 3,经过 2 条通道,长度 = 2)。

如果起点终点相同,返回 0;如果房间无法到达,返回 −1。

二、输入描述

顶点数量 n n n:

  • 1 ≤ n ≤ 10000 1 \le n \le 10000 1≤n≤10000
  • 顶点编号:从 1 到 n n n(连续编号)
  • 单顶点情况: n = 1 n = 1 n=1 时,起点终点相同,路径长度为 0

边数量和边参数 edges:

  • 边数量 m m m: 0 ≤ m ≤ min ⁡ ( n ( n − 1 ) 2 , 100000 ) 0 \le m \le \min\left(\frac{n(n-1)}{2},100000\right) 0≤m≤min(2n(n−1)​,100000)
  • 每条边 [ u , v ] [u,v] [u,v]:
  • 1 ≤ u , v ≤ n 1 \le u,v \le n 1≤u,v≤n(顶点编号合法)
  • u u u 和 v v v 可以相等(允许自环,但不影响路径长度)
  • 边是无向的: ( u , v ) (u,v) (u,v) 表示房间 u u u 和房间 v v v 之间有一条双向通道
  • 允许重边:多条边连接同一对顶点(不影响最短路径)

起点和终点参数:

  • 起点 s s s: 1 ≤ s ≤ n 1 \le s \le n 1≤s≤n
  • 终点 t t t: 1 ≤ t ≤ n 1 \le t \le n 1≤t≤n

三、输出描述

返回从起点 s s s 到终点 t t t 的最短路径长度(经过的通道数)。若 s = t s = t s=t 则返回 0;若不可达则返回 −1。

四、测试用例

测试用例1:

1、输入

4
1,2 2,3 3,4 1,3
1 4

2、输出

2

3、说明

最短路径为 1 → 3 → 4,经过 2 条通道;而 1 → 2 → 3 → 4 需要 3 条,因此输出 2。

测试用例2:

1、输入

4
1,2 2,1 3,4 4,3
1 3

2、输出

-1

3、说明

房间 1 和 2 属于一个连通分量,房间 3 和 4 属于另一个连通分量。即使存在反向重复边,也无法从 1 到达 3。

五、解题思路

1. 问题分析

城堡中的每个房间可以看作图中的一个顶点,每条通道可以看作连接两个顶点的无向边。

由于每条通道的长度均为 1,因此本题本质上是求无权无向图中,从起点 s 到终点 t 的最短路径长度。

如果起点和终点相同,直接返回 0;如果起点无法到达终点,则返回 -1。

2. 数据结构选择

使用邻接表存储无向图,使用队列辅助广度优先搜索,使用距离数组 dist 记录最短路径长度。

  • 邻接表:存储每个房间直接连接的其他房间。
  • 队列:保证 BFS 按距离从近到远搜索。
  • 距离数组:初始化为 -1,既表示未访问状态,也记录最短距离。

相比邻接矩阵,邻接表只需 O(n+m) 的空间,更适合 n 最大为 10000、m 最大为 100000 的数据规模。

3. 算法步骤

  1. 读取房间数量 n、边列表、起点 s 和终点 t。
  2. 如果 s == t,直接输出 0。
  3. 根据边列表构建无向图,每条有效边需要双向添加。
  4. 初始化距离数组为 -1,设置 dist[s]=0,并将 s 加入队列。
  5. 从队列取出当前房间,遍历其相邻房间。
  6. 如果相邻房间尚未访问,则将其距离设置为当前距离加 1,并加入队列。
  7. 如果首次发现终点 t,立即返回对应距离。
  8. 如果队列为空仍未找到终点,输出 -1。

4. 正确性分析

BFS 按路径长度逐层扩展。由于所有边的长度均为 1,首次访问一个房间时,所经过的通道数一定最少。

因此,首次到达终点时得到的距离就是最短路径长度。

自环不影响最短路径,可以忽略;重边通过距离数组避免重复访问,不影响结果。

5. 复杂度分析

  • 时间复杂度:O(n+m),每个顶点最多入队一次,每条边最多检查两次。
  • 空间复杂度:O(n+m),主要用于邻接表、距离数组及 BFS 队列。

该算法能够高效处理题目规定的数据规模。

六、Java算法源码


import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Queue;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

        // 输入固定为三行:房间数量、边列表、起点和终点。
        // 第二行可能为空,表示没有通道,因此不能用 next() 读取边列表。
        int n = Integer.parseInt(scanner.nextLine().trim());
        String edgesLine = scanner.nextLine();
        int s = scanner.nextInt();
        int t = scanner.nextInt();

        if (s == t) {
            System.out.println(0);
            scanner.close();
            return;
        }

        // 使用邻接表存储无向图,空间复杂度 O(n + m)。
        // 正则兼容 1,2、1, 2 以及边与边之间的多个空格。
        List<List<Integer>> graph = new ArrayList<>(n + 1);
        for (int i = 0; i <= n; i++) {
            graph.add(new ArrayList<>());
        }

        Matcher matcher = Pattern
                .compile("(\\d+)\\s*,\\s*(\\d+)")
                .matcher(edgesLine);

        while (matcher.find()) {
            int u = Integer.parseInt(matcher.group(1));
            int v = Integer.parseInt(matcher.group(2));

            // 自环不会缩短到其他房间的路径,直接忽略。
            if (u != v) {
                graph.get(u).add(v);
                graph.get(v).add(u);
            }
        }

        // BFS 按经过通道的数量逐层扩展,
        // 首次到达终点即得到最短路径。
        // dist 同时记录距离和是否访问:
        // -1 表示尚未访问。
        int[] dist = new int[n + 1];
        Arrays.fill(dist, -1);

        Queue<Integer> queue = new ArrayDeque<>();
        dist[s] = 0;
        queue.offer(s);

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

            for (int v : graph.get(u)) {
                if (dist[v] != -1) {
                    // 防止无向边反复回走,
                    // 重边也不会造成重复入队。
                    continue;
                }

                dist[v] = dist[u] + 1;

                if (v == t) {
                    System.out.println(dist[v]);
                    scanner.close();
                    return;
                }

                queue.offer(v);
            }
        }

        // BFS 结束仍未找到终点,说明不可达。
        System.out.println(-1);
        scanner.close();
    }
}

七、效果展示

1、输入

6
1,2 2,3 1,4 4,5 5,6 3,6 2,2
1 6

2、输出

3

3、说明

可以沿着 1 → 2 → 3 → 6 或 1 → 4 → 5 → 6 到达终点,两条路径长度均为 3。房间 2 的自环不影响结果。

在这里插入图片描述


🏆下一篇:华为OD机试 - 简易内存池 - 逻辑分析(Java 新系统 200分)

🏆本专栏收录于《华为OD机试(JAVA)真题》。

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天优快云在线答疑。

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪 吒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值