一、问题描述
题目描述
有一个大小为 N * M
的战场地图,被墙壁 #
分隔成大小不同的区域。上下左右四个方向相邻的空地 .
属于同一个区域,只有空地上可能存在敌人 E
。请求出地图上总共有多少区域里的敌人数小于 K
。
输入描述
- 第一行输入为
N, M, K
:N
表示地图的行数。M
表示地图的列数。K
表示目标敌人数量。- 满足
N, M <= 100
。
- 之后为一个
N x M
大小的字符数组,表示地图。
输出描述
- 输出敌人数小于
K
的区域数量。
用例
用例1
输入
3 5 2
..#EE
E.#E.
###..
输出
1
说明
地图被墙壁分为两个区域:
- 左边区域有
1
个敌人。 - 右边区域有
3
个敌人。
符合条件的区域数量是1
。
题目解析
问题分析
-
地图表示:
- 地图由字符数组表示,包含以下字符:
#
:墙壁,分隔区域。.
:空地,属于某个区域。E
:敌人,位于空地上。
- 地图由字符数组表示,包含以下字符:
-
区域定义:
- 上下左右四个方向相邻的空地
.
属于同一个区域。 - 每个区域内的敌人
E
需要统计。
- 上下左右四个方向相邻的空地
-
目标:
- 统计所有区域中,敌人数小于
K
的区域数量。
- 统计所有区域中,敌人数小于
解题思路
-
遍历地图:
- 遍历地图的每个点,如果该点是空地
.
或敌人E
,并且未被访问过,则从该点开始搜索整个区域。
- 遍历地图的每个点,如果该点是空地
-
搜索区域:
- 使用 深度优先搜索(DFS) 或 广度优先搜索(BFS) 遍历当前区域的所有点。
- 在搜索过程中,统计区域内的敌人数量。
-
判断条件:
- 如果当前区域的敌人数小于
K
,则符合条件的区域数量加1
。
- 如果当前区域的敌人数小于
-
标记访问:
- 在搜索过程中,标记已访问的点,避免重复搜索。
关键点
-
搜索算法选择:
- 由于地图大小可能较大(
N, M <= 100
),推荐使用 广度优先搜索(BFS),避免递归深度过大导致栈溢出。
- 由于地图大小可能较大(
-
区域边界:
- 在搜索时,需要判断当前点是否在地图范围内,并且不是墙壁
#
。
- 在搜索时,需要判断当前点是否在地图范围内,并且不是墙壁
-
敌人统计:
- 在搜索过程中,如果遇到敌人
E
,则当前区域的敌人数加1
。
- 在搜索过程中,如果遇到敌人
-
访问标记:
- 使用一个二维数组记录每个点是否被访问过,避免重复搜索。
算法步骤
-
初始化:
- 读取输入,初始化地图和访问标记数组。
-
遍历地图:
- 遍历地图的每个点,如果该点是空地
.
或敌人E
,并且未被访问过,则从该点开始搜索。
- 遍历地图的每个点,如果该点是空地
-
搜索区域:
- 使用 BFS 或 DFS 遍历当前区域的所有点。
- 统计区域内的敌人数量。
-
判断条件:
- 如果当前区域的敌人数小于
K
,则符合条件的区域数量加1
。
- 如果当前区域的敌人数小于
-
输出结果:
- 输出符合条件的区域数量。
复杂度分析
-
时间复杂度:
- 每个点最多被访问一次,因此时间复杂度为
O(N * M)
。
- 每个点最多被访问一次,因此时间复杂度为
-
空间复杂度:
- 需要额外的空间存储访问标记数组,空间复杂度为
O(N * M)
。
- 需要额外的空间存储访问标记数组,空间复杂度为
总结
- 本题是一个典型的 连通区域统计 问题,可以通过 BFS 或 DFS 解决。
- 需要注意地图边界和墙壁的处理,以及敌人数量的统计。
- 由于地图大小可能较大,推荐使用 BFS 避免栈溢出问题。
二、JavaScript算法源码
广度优先搜索(基于队列结构,先进先出)
代码解析
输入处理部分
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const lines = [];
// 矩阵行数n,列数m,区域敌军数量上限k
let n, m, k;
// 矩阵表示地图
let matrix;
// 访问矩阵
let visited;
// 监听每行输入
rl.on("line", (line) => {
lines.push(line);
// 第一行输入矩阵大小n、m和敌军数量上限k
if (lines.length == 1) {
[n, m, k] = lines[0].split(" ").map(Number);
}
// 当矩阵输入完成时触发逻辑
if (n != undefined && lines.length == n + 1) {
lines.shift(); // 移除第一行
matrix = lines; // 剩下的行构成地图矩阵
visited = new Array(n).fill(0).map(() => new Array(m).fill(false)); // 初始化访问矩阵
console.log(getResult()); // 调用主函数,计算结果并输出
lines.length = 0; // 清空输入缓存
}
});
- 使用
readline
库模拟竞赛中常见的多行输入模式。 - 第一行是矩阵大小(
n
和m
)以及敌军数量上限(k
)。 - 后续是地图数据矩阵(
matrix
)。 - 一个二维布尔数组
visited
用来标记每个点是否已经访问过。
主逻辑函数
function getResult() {
let ans = 0; // 符合条件的区域计数
for (let i = 0; i < n; i++) {
for (let j = 0; j < m; j++) {
// 跳过已访问过的点或墙(#)
if (visited[i][j] || matrix[i][j] === "#") continue;
// 计算区域内敌军数量,如果小于k,则该区域符合要求
ans += bfs(i, j) < k ? 1 : 0;
}
}
return ans;
}
- 遍历矩阵的每一个点
(i, j)
:- 如果点未访问过并且不是墙(
#
),调用bfs
进行广度优先搜索。 - 如果该区域敌军数量小于
k
,计数器ans
加一。
- 如果点未访问过并且不是墙(
广度优先搜索(BFS)函数
const offsets = [
[-1, 0], // 上
[1, 0], // 下
[0, -1], // 左
[0, 1], // 右
];
function bfs(i, j) {
let count = 0; // 当前区域敌军数量
// 如果起点是敌军(E),敌军数量+1
if (matrix[i][j] === "E") count += 1;
// 标记起点已访问
visited[i][j] = true;
// 初始化队列,存放待处理的节点
const queue = [[i, j]];
while (queue.length > 0) {
const [x, y] = queue.shift(); // 取队首元素
// 遍历四个方向
for (let offset of offsets) {
const newX = x + offset[0];
const newY = y + offset[1];
// 检查新位置是否符合条件
if (
newX >= 0 &&
newX < n && // 不越界
newY >= 0 &&
newY < m &&
!visited[newX][newY] && // 未访问过
matrix[newX][newY] !== "#" // 不是墙
) {
if (matrix[newX][newY] === "E") count += 1; // 如果是敌军,敌军数量+1
visited[newX][newY] = true; // 标记访问
queue.push([newX, newY]); // 入队
}
}
}
return count; // 返回区域内敌军数量
}
- 利用队列(FIFO)实现广度优先搜索。
- 从起点
(i, j)
开始,依次处理所有与当前区域连通的节点:- 如果新节点是敌军(
E
),增加敌军计数。 - 将符合条件的节点加入队列,作为下一步处理的起点。
- 如果新节点是敌军(
深度优先搜索(基于栈结构,后进先出)
代码解析
与 BFS 相似,只是使用栈(LIFO)替代队列。
深搜主函数
function dfs(i, j) {
let count = 0; // 当前区域敌军数量
if (matrix[i][j] === "E") count += 1; // 如果起点是敌军,敌军数量+1
visited[i][j] = true; // 标记起点已访问
const stack = [[i, j]]; // 初始化栈
while (stack.length > 0) {
const [x, y] = stack.pop(); // 取栈顶元素
// 遍历四个方向
for (let offset of offsets) {
const newX = x + offset[0];
const newY = y + offset[1];
// 检查新位置是否符合条件
if (
newX >= 0 &&
newX < n &&
newY >= 0 &&
newY < m &&
!visited[newX][newY] &&
matrix[newX][newY] !== "#"
) {
if (matrix[newX][newY] === "E") count += 1; // 如果是敌军,敌军数量+1
visited[newX][newY] = true; // 标记访问
stack.push([newX, newY]); // 入栈
}
}
}
return count; // 返回区域内敌军数量
}
- 栈的操作与队列类似,只是换成了“后进先出”。
- 每次从栈顶取出节点,处理其四周的有效节点。
深度优先搜索(基于递归)
代码解析
深搜递归版
function dfs(x, y, count) {
if (matrix[x][y] === "E") count += 1; // 如果当前点是敌军,敌军数量+1
visited[x][y] = true; // 标记当前点已访问
for (let offset of offsets) {
const newX = x + offset[0];
const newY = y + offset[1];
// 检查新位置是否符合条件
if (
newX >= 0 &&
newX < n &&
newY >= 0 &&
newY < m &&
!visited[newX][newY] &&
matrix[newX][newY] !== "#"
) {
count = dfs(newX, newY, count); // 递归调用
}
}
return count; // 返回区域内敌军数量
}
- 递归版深搜逻辑与栈版深搜的核心思想一致:
- 每次处理当前节点的四周,递归进入尚未访问的有效节点。
- 但递归实现容易导致栈溢出(
Stack Overflow
),尤其是区域过大时。
总结
-
广度优先搜索(BFS):
- 利用队列,逐层扩展,适合寻找最短路径等问题。
- 时间复杂度:(O(n \times m)),空间复杂度:(O(n \times m))。
-
深度优先搜索(DFS):
- 可以使用栈模拟或递归实现,适合寻找连通区域等问题。
- 时间复杂度:(O(n \times m)),空间复杂度:栈版为 (O(n \times m)),递归版为递归深度。
-
递归版深搜:
- 实现简洁,但大规模地图可能导致栈溢出,实际应用中应尽量避免。
以下是针对 广度优先搜索 (BFS)、基于栈的深度优先搜索 (DFS - 栈实现) 和 基于递归的深度优先搜索 (DFS - 递归实现) 的对比表格,涵盖了其核心特点、优缺点、适用场景等多个维度:
算法类型 | 实现方式 | 核心数据结构 | 特点 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|---|---|---|---|
广度优先搜索 (BFS) | 队列(先进先出,FIFO) | 队列 | 按照层次逐层扩展,先处理距离起点较近的节点。 | (O(N \times M)) | (O(N \times M)) | - 找最短路径时非常高效 - 遍历连通区域不会漏点 - 扩展层次明确,逻辑清晰。 | - 队列可能消耗较多内存 - 在复杂地图中,空间开销可能较大。 | - 迷宫最短路径求解 - 连通区域搜索 - 层次遍历或分层问题。 |
深度优先搜索 (DFS) | 栈(后进先出,LIFO) | 栈 | 沿着路径走到尽头再回溯,探索新的路径,按路径的深度逐步扩展。 | (O(N \times M)) | (O(N \times M)) | - 实现简单,逻辑清晰 - 没有递归调用,避免了递归深度限制问题。 | - 可能会深入到不必要的路径,遍历效率低于 BFS - 栈可能消耗较多内存。 | - 连通区域搜索 - 迷宫全路径搜索 - 拓扑排序、连通分量分析。 |
深度优先搜索 (DFS) | 递归(函数调用栈,隐式实现) | 递归调用栈 | 沿着路径递归调用,先深入某一方向到底再回溯,方法自然简洁。 | (O(N \times M)) | 最坏 (O(N \times M)) | - 实现简洁,代码量少 - 不需要手动维护栈,适合小规模递归问题。 | - 容易导致栈溢出(Stack Overflow) - 无法处理较大的地图,栈深度可能不够支持复杂结构。 | - 小规模连通区域搜索 - 迷宫小规模路径搜索 - 适合自然递归的问题,例如树的遍历。 |
说明与解释
-
算法实现方式:
- BFS 使用队列(FIFO),通过不断扩展当前层的节点逐步深入。
- DFS(栈实现)通过手动维护栈模拟递归行为,实现路径的深度优先搜索。
- DFS(递归实现)直接利用函数调用栈,避免手动维护数据结构。
-
核心数据结构:
- BFS 依赖队列。
- DFS(栈实现)依赖栈。
- DFS(递归实现)依赖函数调用栈(隐式栈)。
-
时间复杂度:
- 三者的时间复杂度均为 (O(N \times M)),其中 (N) 是矩阵的行数,(M) 是列数。因为每个点最多访问一次,且每次处理上下左右四个方向。
-
空间复杂度:
- BFS 和 DFS(栈实现)显式维护一个队列或栈,因此空间复杂度取决于队列或栈的最大长度。
- DFS(递归实现)依赖隐式函数调用栈,最坏情况下递归深度为 (N \times M)。
-
优点:
- BFS:适合寻找最短路径、层次结构等问题,逻辑清晰,易于维护。
- DFS(栈实现):避免了递归深度限制问题,适合大规模问题。
- DFS(递归实现):实现简洁,是递归场景的自然选择。
-
缺点:
- BFS:队列可能占用较大的额外空间。
- DFS(栈实现):手动维护栈结构,代码稍显复杂。
- DFS(递归实现):递归深度受限,可能导致栈溢出,需谨慎处理大规模问题。
-
适用场景:
- BFS:最短路径、多层次问题(如迷宫搜索)。
- DFS(栈实现):连通区域分析、全路径搜索(如统计岛屿数量、生成迷宫)。
- DFS(递归实现):适合小规模或递归深度较少的问题(如树的遍历)。
上述代码的处理逻辑清晰,适用于连通区域搜索问题,例如迷宫、岛屿计数等。
三、Java算法源码
以下是广度优先搜索(BFS)、深度优先搜索(DFS - 栈实现)以及深度优先搜索(DFS - 递归实现)的 Java 代码:
广度优先搜索(基于队列结构,先进先出)
代码解析
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
static int n; // 地图行数
static int m; // 地图列数
static int k; // 区域敌军人数上限值
static char[][] matrix; // 地图矩阵
static boolean[][] visited; // 访问矩阵
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 读取地图的大小和敌军人数上限
int[] tmp = Arrays.stream(sc.nextLine().split(" "))
.mapToInt(Integer::parseInt).toArray();
n = tmp[0];
m = tmp[1];
k = tmp[2];
visited = new boolean[n][m]; // 初始化访问矩阵
// 读取地图矩阵
matrix = new char[n][];
for (int i = 0; i < n; i++) {
matrix[i] = sc.nextLine().toCharArray();
}
// 输出符合条件的区域数量
System.out.println(getResult());
}
public static int getResult() {
int ans = 0; // 符合条件的区域计数
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 检查每个位置,如果未访问且不是墙,则进行广度优先搜索
if (visited[i][j] || matrix[i][j] == '#') continue;
// 如果该区域的敌军人数小于 k,则计数加一
ans += bfs(i, j) < k ? 1 : 0;
}
}
return ans;
}
// 上、下、左、右偏移量
static int[][] offsets = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public static int bfs(int i, int j) {
int count = 0; // 当前区域敌军数量
visited[i][j] = true; // 标记当前节点已访问
if (matrix[i][j] == 'E') count += 1; // 如果当前位置是敌军,计数加一
LinkedList<int[]> queue = new LinkedList<>(); // 使用链表模拟队列
queue.add(new int[] {i, j});
while (queue.size() > 0) {
int[] pos = queue.removeFirst(); // 取出队首元素
int x = pos[0];
int y = pos[1];
// 遍历当前节点的上下左右位置
for (int[] offset : offsets) {
int newX = x + offset[0];
int newY = y + offset[1];
// 检查新位置是否符合条件
if (newX >= 0 && newX < n && newY >= 0 && newY < m && !visited[newX][newY] && matrix[newX][newY] != '#') {
visited[newX][newY] = true; // 标记新位置已访问
if (matrix[newX][newY] == 'E') count += 1; // 如果是敌军,计数加一
queue.add(new int[] {newX, newY}); // 将新位置加入队列
}
}
}
return count; // 返回区域内敌军数量
}
}
- 输入处理:使用
Scanner
读取输入,初始化地图大小和敌军上限。 - BFS 实现:
- 使用
LinkedList
作为队列,逐层扩展节点。 - 每次从队首取出节点,访问其四周未访问且不是墙的位置。
- 计算当前区域的敌军数量,返回计数结果。
- 使用
深度优先搜索(基于栈结构,后进先出)
代码解析
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
static int n; // 地图行数
static int m; // 地图列数
static int k; // 区域敌军人数上限值
static char[][] matrix; // 地图矩阵
static boolean[][] visited; // 访问矩阵
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 读取地图的大小和敌军人数上限
int[] tmp = Arrays.stream(sc.nextLine().split(" "))
.mapToInt(Integer::parseInt).toArray();
n = tmp[0];
m = tmp[1];
k = tmp[2];
visited = new boolean[n][m]; // 初始化访问矩阵
// 读取地图矩阵
matrix = new char[n][];
for (int i = 0; i < n; i++) {
matrix[i] = sc.nextLine().toCharArray();
}
// 输出符合条件的区域数量
System.out.println(getResult());
}
public static int getResult() {
int ans = 0; // 符合条件的区域计数
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 检查每个位置,如果未访问且不是墙,则进行深度优先搜索
if (visited[i][j] || matrix[i][j] == '#') continue;
// 如果该区域的敌军人数小于 k,则计数加一
ans += dfs(i, j) < k ? 1 : 0;
}
}
return ans;
}
// 上、下、左、右偏移量
static int[][] offsets = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public static int dfs(int i, int j) {
int count = 0; // 当前区域敌军数量
visited[i][j] = true; // 标记当前节点已访问
if (matrix[i][j] == 'E') count += 1; // 如果当前位置是敌军,计数加一
LinkedList<int[]> stack = new LinkedList<>(); // 使用链表模拟栈
stack.add(new int[] {i, j});
while (stack.size() > 0) {
int[] pos = stack.removeLast(); // 取出栈顶元素
int x = pos[0];
int y = pos[1];
// 遍历当前节点的上下左右位置
for (int[] offset : offsets) {
int newX = x + offset[0];
int newY = y + offset[1];
// 检查新位置是否符合条件
if (newX >= 0 && newX < n && newY >= 0 && newY < m && !visited[newX][newY] && matrix[newX][newY] != '#') {
visited[newX][newY] = true; // 标记新位置已访问
if (matrix[newX][newY] == 'E') count += 1; // 如果是敌军,计数加一
stack.add(new int[] {newX, newY}); // 将新位置加入栈
}
}
}
return count; // 返回区域内敌军数量
}
}
- DFS 栈实现:
- 使用
LinkedList
作为栈,通过removeLast
实现后进先出。 - 在每个未访问的有效位置执行 DFS,继续探索其四周位置,计算敌军数量。
- 使用
深度优先搜索(基于递归)
代码解析
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static int n; // 地图行数
static int m; // 地图列数
static int k; // 区域敌军人数上限值
static char[][] matrix; // 地图矩阵
static boolean[][] visited; // 访问矩阵
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 读取地图的大小和敌军人数上限
int[] tmp = Arrays.stream(sc.nextLine().split(" "))
.mapToInt(Integer::parseInt).toArray();
n = tmp[0];
m = tmp[1];
k = tmp[2];
visited = new boolean[n][m]; // 初始化访问矩阵
// 读取地图矩阵
matrix = new char[n][];
for (int i = 0; i < n; i++) {
matrix[i] = sc.nextLine().toCharArray();
}
// 输出符合条件的区域数量
System.out.println(getResult());
}
public static int getResult() {
int ans = 0; // 符合条件的区域计数
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 检查每个位置,如果未访问且不是墙,则进行深度优先搜索
if (visited[i][j] || matrix[i][j] == '#') continue;
// 如果该区域的敌军人数小于 k,则计数加一
ans += dfs(i, j, 0) < k ? 1 : 0;
}
}
return ans;
}
// 上、下、左、右偏移量
static int[][] offsets = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public static int dfs(int x, int y, int count) {
if (matrix[x][y] == 'E') count += 1; // 如果当前位置是敌军,计数加一
visited[x][y] = true; // 标记当前节点已访问
// 遍历当前节点的上下左右位置
for (int[] offset : offsets) {
int newX = x + offset[0];
int newY = y + offset[1];
// 检查新位置是否符合条件
if (newX >= 0 && newX < n && newY >= 0 && newY < m && !visited[newX][newY] && matrix[newX][newY] != '#') {
count = dfs(newX, newY, count); // 递归调用 dfs
}
}
return count; // 返回区域内敌军数量
}
}
- DFS 递归实现:
- 通过递归调用实现深度优先搜索。
- 对于每个符合条件的位置,递归进入未访问的邻接位置,累积敌军数量。
- 适用于小规模问题,但需注意递归深度可能导致栈溢出。
总结
- BFS 使用队列:逐层扩展,适合寻找最短路径或同层次节点的问题。
- DFS 使用栈:手动维护栈实现路径探索,避免了递归深度限制。
- DFS 使用递归:实现简洁,适合小规模问题,需注意递归深度限制。
下面是针对广度优先搜索(BFS)、深度优先搜索(DFS - 栈实现)、和深度优先搜索(DFS - 递归实现)的对比表格,涵盖了它们的核心特点、优缺点和适用场景:
算法类型 | 实现方式 | 核心数据结构 | 特点 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|---|---|---|---|
广度优先搜索 (BFS) | 队列(先进先出,FIFO) | 队列 | 按照层次逐层扩展,先处理距离起点较近的节点。 | (O(N \times M)) | (O(N \times M)) | - 高效处理最短路径问题 - 层次结构清晰 - 不会遗漏任何节点。 | - 队列可能消耗较多内存,尤其在大规模地图中。 | - 迷宫最短路径求解 - 层次遍历或多层次问题。 |
深度优先搜索 (DFS) | 栈(后进先出,LIFO) | 栈 | 沿路径深入到底再回溯,按路径深度逐步扩展。 | (O(N \times M)) | (O(N \times M)) | - 适合连通区域搜索 - 没有递归限制 - 效率高于递归实现。 | - 可能深入不必要路径,遍历效率不如 BFS。 | - 连通区域搜索 - 全路径搜索。 |
深度优先搜索 (DFS) | 递归(函数调用栈,隐式实现) | 函数调用栈 | 沿路径递归调用,先深入后回溯,方法自然简洁。 | (O(N \times M)) | 最坏 (O(N \times M)) | - 代码简洁 - 自然适用于递归结构,如树的遍历。 | - 容易导致栈溢出(Stack Overflow),不适合大规模或深度大的问题。 | - 小规模连通区域搜索 - 适合自然递归的问题。 |
说明与解释
-
算法实现方式:
- BFS 使用队列(FIFO),逐层扩展节点,适合处理层次结构和最短路径类问题。
- DFS(栈实现)通过手动维护栈模拟递归行为,适合连通区域搜索。
- DFS(递归实现)直接利用函数调用栈,代码简洁,适合小规模递归问题。
-
核心数据结构:
- BFS 依赖显式的队列管理节点。
- DFS(栈实现)依赖显式的栈管理节点。
- DFS(递归实现)依赖隐式的函数调用栈。
-
时间和空间复杂度:
- 三者的时间复杂度均为 (O(N \times M)),因为每个节点最多访问一次。
- BFS 和 DFS(栈实现)的空间复杂度为 (O(N \times M))。
- DFS(递归实现)的空间复杂度取决于递归深度,最坏情况下 (O(N \times M))。
-
优点和缺点:
- BFS:层次清晰,适合最短路径,但可能有较高的空间开销。
- DFS 栈实现:避免递归限制,适合大规模问题,但比 BFS 更可能深入不必要路径。
- DFS 递归实现:实现简洁,但可能导致栈溢出,不适合大规模问题。
-
适用场景:
- BFS:用于最短路径、层次遍历。
- DFS 栈实现:用于连通区域分析、路径搜索。
- DFS 递归实现:适合小规模问题或递归结构较简单的问题。
以上代码结构和注释帮助理解不同搜索算法在解决连通区域问题时的应用和特点。
四、Python算法源码
以下是广度优先搜索(BFS)、深度优先搜索(DFS - 栈实现)、深度优先搜索(DFS - 递归实现)代码
广度优先搜索(基于队列结构,先进先出)
代码
# 输入获取
n, m, k = map(int, input().split()) # 地图行数, 地图列数, 区域敌军人数上限值
matrix = [input() for _ in range(n)] # 地图矩阵
visited = [[False for _ in range(m)] for _ in range(n)] # 访问矩阵,初始状态均为False
offsets = ((-1, 0), (1, 0), (0, -1), (0, 1)) # 上、下、左、右偏移量,用于遍历当前点的四个方向
# 广度优先搜索
def bfs(i, j):
count = 0 # 记录该区域内敌军数量
# 如果当前位置是敌军(E),计数加1
if matrix[i][j] == 'E':
count += 1
# 标记当前位置已访问
visited[i][j] = True
# 初始化队列,广度优先搜索依赖队列,先进先出
queue = [(i, j)]
while len(queue) > 0:
# 取出队首节点
x, y = queue.pop(0)
# 遍历当前节点的上下左右位置
for offsetX, offsetY in offsets:
newX = x + offsetX
newY = y + offsetY
# 判断条件:新位置不越界,未访问过,且不是墙
if n > newX >= 0 and m > newY >= 0 and not visited[newX][newY] and matrix[newX][newY] != '#':
# 如果新位置是敌军(E),计数加1
if matrix[newX][newY] == 'E':
count += 1
# 标记新位置已访问
visited[newX][newY] = True
# 将新位置加入队列
queue.append((newX, newY))
return count # 返回当前区域的敌军数量
# 算法入口
def getResult():
ans = 0 # 记录符合条件的区域的数量
# 遍历地图的每个位置
for i in range(n):
for j in range(m):
# 如果当前位置已经访问过或是墙,则跳过
if visited[i][j] or matrix[i][j] == '#':
continue
# 如果当前位置未访问且不是墙,进行广搜;若该区域敌军数量小于k,则计入符合条件的区域
ans += 1 if bfs(i, j) < k else 0
return ans # 返回符合条件的区域数量
# 算法调用
print(getResult())
核心逻辑
- 使用队列实现广度优先搜索,逐层遍历。
- 每次访问一个位置,将其四周符合条件的位置加入队列。
- 记录每个区域的敌军数量,判断是否小于
k
。
深度优先搜索(基于栈结构,后进先出)
代码
# 输入获取
n, m, k = map(int, input().split()) # 地图行数, 地图列数, 区域敌军人数上限值
matrix = [input() for _ in range(n)] # 地图矩阵
visited = [[False for _ in range(m)] for _ in range(n)] # 访问矩阵
offsets = ((-1, 0), (1, 0), (0, -1), (0, 1)) # 上、下、左、右偏移量
# 深度优先搜索
def dfs(i, j):
count = 0 # 记录该区域敌军数量
visited[i][j] = True # 标记当前位置已访问
if matrix[i][j] == 'E': # 如果当前位置是敌军,计数加1
count += 1
# 初始化栈,深度优先搜索依赖栈,后进先出
stack = [(i, j)]
while len(stack) > 0:
# 取出栈顶节点
x, y = stack.pop()
# 遍历当前节点的上下左右位置
for offsetX, offsetY in offsets:
newX = x + offsetX
newY = y + offsetY
# 判断条件:新位置不越界,未访问过,且不是墙
if n > newX >= 0 and m > newY >= 0 and not visited[newX][newY] and matrix[newX][newY] != '#':
visited[newX][newY] = True # 标记新位置已访问
if matrix[newX][newY] == 'E': # 如果新位置是敌军,计数加1
count += 1
stack.append((newX, newY)) # 将新位置加入栈
return count # 返回当前区域的敌军数量
# 算法入口
def getResult():
ans = 0 # 记录符合条件的区域数量
# 遍历地图的每个位置
for i in range(n):
for j in range(m):
# 如果当前位置已经访问过或是墙,则跳过
if visited[i][j] or matrix[i][j] == '#':
continue
# 如果当前位置未访问且不是墙,进行深搜;若该区域敌军数量小于k,则计入符合条件的区域
ans += 1 if dfs(i, j) < k else 0
return ans # 返回符合条件的区域数量
# 算法调用
print(getResult())
核心逻辑
- 使用栈实现深度优先搜索,每次从栈顶取出一个位置,遍历其四周。
- 保证深度优先的方向,直到某条路径走完才回溯。
深度优先搜索(基于递归实现)
代码
# 输入获取
n, m, k = map(int, input().split()) # 地图行数, 地图列数, 区域敌军人数上限值
matrix = [input() for _ in range(n)] # 地图矩阵
visited = [[False for _ in range(m)] for _ in range(n)] # 访问矩阵
offsets = ((-1, 0), (1, 0), (0, -1), (0, 1)) # 上、下、左、右偏移量
# 深度优先搜索(递归实现)
def dfs(x, y, count):
# 如果当前位置是敌军(E),计数加1
if matrix[x][y] == 'E':
count += 1
# 标记当前位置已访问
visited[x][y] = True
# 遍历当前节点的上下左右位置
for offsetX, offsetY in offsets:
newX = x + offsetX
newY = y + offsetY
# 判断条件:新位置不越界,未访问过,且不是墙
if n > newX >= 0 and m > newY >= 0 and not visited[newX][newY] and matrix[newX][newY] != '#':
count = dfs(newX, newY, count) # 递归调用,继续搜索
return count # 返回当前区域敌军数量
# 算法入口
def getResult():
ans = 0 # 记录符合条件的区域数量
# 遍历地图的每个位置
for i in range(n):
for j in range(m):
# 如果当前位置已经访问过或是墙,则跳过
if visited[i][j] or matrix[i][j] == '#':
continue
# 如果当前位置未访问且不是墙,进行深搜;若该区域敌军数量小于k,则计入符合条件的区域
ans += 1 if dfs(i, j, 0) < k else 0
return ans # 返回符合条件的区域数量
# 算法调用
print(getResult())
核心逻辑
- 使用递归实现深度优先搜索,每次调用函数处理当前节点,然后递归处理其四周节点。
- 递归返回时累计敌军数量。
以下是针对广度优先搜索(BFS)、深度优先搜索(DFS - 基于栈实现)、深度优先搜索(DFS - 基于递归实现)的表格,涵盖它们的核心特点、实现细节、适用场景和代码逻辑:
算法类型 | 广度优先搜索(BFS) | 深度优先搜索(DFS - 栈实现) | 深度优先搜索(DFS - 递归实现) |
---|---|---|---|
核心数据结构 | 队列(FIFO,先进先出) | 栈(LIFO,后进先出) | 函数调用栈(递归实现,隐式栈) |
遍历顺序 | 按层级逐层遍历,从起始节点开始,逐步推进至更远的节点 | 沿某一条路径深入到底,再回溯,继续寻找其他路径 | 沿某一条路径递归深入到底,再回溯,继续寻找其他路径 |
实现方式 | 显式使用 queue 队列记录待访问节点 | 显式使用 stack 栈记录待访问节点 | 隐式依赖函数调用栈 |
代码复杂度 | 使用队列实现,代码稍复杂,需手动管理队列 | 使用栈实现,代码稍复杂,需手动管理栈 | 代码简洁,直接递归调用,但需考虑递归深度 |
时间复杂度 | (O(N \times M)) | (O(N \times M)) | (O(N \times M)) |
空间复杂度 | 最坏情况下 (O(N \times M)),取决于队列大小 | 最坏情况下 (O(N \times M)),取决于栈大小 | 最坏情况下 (O(N \times M)),取决于递归深度 |
适用场景 | - 计算最短路径(例如迷宫问题) - 图的分层遍历 | - 连通区域搜索 - 图的全路径遍历 | - 小规模连通区域搜索 - 适合递归结构 |
优点 | - 能找到最短路径 - 层次结构清晰 | - 无递归限制 - 适合需要手动维护的场景 | - 代码简洁 - 自然适用于递归结构 |
缺点 | - 空间占用高(队列可能很大) | - 栈可能消耗内存,但比递归稳定 | - 递归深度过大可能导致栈溢出 |
实现逻辑 | 1. 初始化队列并将起点加入队列 2. 按层次依次访问节点 | 1. 初始化栈并将起点加入栈 2. 按路径深度访问节点 | 1. 递归处理当前节点 2. 对子节点递归调用 |
代码调用方式 | bfs(i, j) | dfs(i, j) | dfs(i, j, count) |
适用地图特点 | - 需要层次遍历或最短路径的网格 | - 需要全路径搜索或连通区域搜索的网格 | - 适合规模较小的网格,递归深度不大的场景 |
表格核心解释
-
核心数据结构:
- BFS 依赖外部显式的队列(FIFO),实现逐层遍历。
- DFS(栈实现)依赖外部显式的栈(LIFO),实现路径深度优先遍历。
- DFS(递归实现)依赖隐式的函数调用栈,递归处理节点。
-
遍历顺序:
- BFS 按照距离起点的层次逐层扩展。
- DFS(栈实现)和 DFS(递归实现)优先探索一条路径到底,再回溯探索其他路径。
-
时间与空间复杂度:
- 三种方法时间复杂度都是 (O(N \times M)),因为每个节点最多访问一次。
- 空间复杂度在最坏情况下都要存储所有节点((O(N \times M))),但 BFS 使用队列,DFS 使用栈或递归调用栈。
-
优缺点:
- BFS 更适合找到最短路径,但空间开销大。
- DFS(栈实现)和 DFS(递归实现)更适合全路径搜索,递归实现代码简洁,但需注意递归深度。
-
适用场景:
- BFS 更适合迷宫最短路径问题。
- DFS 更适合连通区域搜索和全路径问题,递归实现适合小规模问题。
以上代码展示了 BFS、DFS(栈实现)、DFS(递归实现)的完整逻辑和详细注释,便于理解其工作原理和实现方式。
五、C/C++算法源码:
C++ 版本
广度优先搜索(BFS)
#include <iostream>
#include <queue>
using namespace std;
const int MAX_SIZE = 101;
// 地图行数, 地图列数, 区域敌军人数上限值
int n, m, k;
// 地图矩阵
char matrix[MAX_SIZE][MAX_SIZE] = {'\0'};
// 访问矩阵
bool visited[MAX_SIZE][MAX_SIZE] = {false};
// 上下左右的方位偏移量
int offsets[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// BFS函数,返回当前区域的敌人数
int bfs(int i, int j) {
int count = 0; // 当前区域的敌人数
queue<pair<int, int>> q; // 队列,用于BFS
// 标记起点为已访问
visited[i][j] = true;
// 如果起点是敌人,敌人数加1
if (matrix[i][j] == 'E') count++;
// 将起点加入队列
q.push({i, j});
// BFS遍历
while (!q.empty()) {
auto [x, y] = q.front(); // 取出队首元素
q.pop();
// 遍历上下左右四个方向
for (int a = 0; a < 4; a++) {
int newX = x + offsets[a][0];
int newY = y + offsets[a][1];
// 检查新位置是否合法且未被访问过,且不是墙
if (newX >= 0 && newX < n && newY >= 0 && newY < m && !visited[newX][newY] && matrix[newX][newY] != '#') {
// 标记为已访问
visited[newX][newY] = true;
// 如果是敌人,敌人数加1
if (matrix[newX][newY] == 'E') count++;
// 将新位置加入队列
q.push({newX, newY});
}
}
}
return count;
}
// 主逻辑函数
int getResult() {
int ans = 0; // 符合条件的区域数量
// 遍历地图的每个点
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果该点未被访问过且不是墙,则进行BFS
if (!visited[i][j] && matrix[i][j] != '#') {
int enemyCount = bfs(i, j); // 获取当前区域的敌人数
if (enemyCount < k) ans++; // 如果敌人数小于k,则符合条件
}
}
}
return ans;
}
int main() {
// 读取输入
cin >> n >> m >> k;
for (int i = 0; i < n; i++) {
cin >> matrix[i];
}
// 输出结果
cout << getResult() << endl;
return 0;
}
深度优先搜索(DFS)
#include <iostream>
#include <stack>
using namespace std;
const int MAX_SIZE = 101;
// 地图行数, 地图列数, 区域敌军人数上限值
int n, m, k;
// 地图矩阵
char matrix[MAX_SIZE][MAX_SIZE] = {'\0'};
// 访问矩阵
bool visited[MAX_SIZE][MAX_SIZE] = {false};
// 上下左右的方位偏移量
int offsets[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// DFS函数,返回当前区域的敌人数
int dfs(int i, int j) {
int count = 0; // 当前区域的敌人数
stack<pair<int, int>> st; // 栈,用于DFS
// 标记起点为已访问
visited[i][j] = true;
// 如果起点是敌人,敌人数加1
if (matrix[i][j] == 'E') count++;
// 将起点加入栈
st.push({i, j});
// DFS遍历
while (!st.empty()) {
auto [x, y] = st.top(); // 取出栈顶元素
st.pop();
// 遍历上下左右四个方向
for (int a = 0; a < 4; a++) {
int newX = x + offsets[a][0];
int newY = y + offsets[a][1];
// 检查新位置是否合法且未被访问过,且不是墙
if (newX >= 0 && newX < n && newY >= 0 && newY < m && !visited[newX][newY] && matrix[newX][newY] != '#') {
// 标记为已访问
visited[newX][newY] = true;
// 如果是敌人,敌人数加1
if (matrix[newX][newY] == 'E') count++;
// 将新位置加入栈
st.push({newX, newY});
}
}
}
return count;
}
// 主逻辑函数
int getResult() {
int ans = 0; // 符合条件的区域数量
// 遍历地图的每个点
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果该点未被访问过且不是墙,则进行DFS
if (!visited[i][j] && matrix[i][j] != '#') {
int enemyCount = dfs(i, j); // 获取当前区域的敌人数
if (enemyCount < k) ans++; // 如果敌人数小于k,则符合条件
}
}
}
return ans;
}
int main() {
// 读取输入
cin >> n >> m >> k;
for (int i = 0; i < n; i++) {
cin >> matrix[i];
}
// 输出结果
cout << getResult() << endl;
return 0;
}
C语言版本
广度优先搜索(BFS)
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 101
// 地图行数, 地图列数, 区域敌军人数上限值
int n, m, k;
// 地图矩阵
char matrix[MAX_SIZE][MAX_SIZE] = {'\0'};
// 访问矩阵
int visited[MAX_SIZE][MAX_SIZE] = {0};
// 上下左右的方位偏移量
int offsets[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// BFS函数,返回当前区域的敌人数
int bfs(int i, int j) {
int count = 0; // 当前区域的敌人数
int queue[MAX_SIZE * MAX_SIZE][2]; // 队列,用于BFS
int front = 0, rear = 0;
// 标记起点为已访问
visited[i][j] = 1;
// 如果起点是敌人,敌人数加1
if (matrix[i][j] == 'E') count++;
// 将起点加入队列
queue[rear][0] = i;
queue[rear][1] = j;
rear++;
// BFS遍历
while (front < rear) {
int x = queue[front][0];
int y = queue[front][1];
front++;
// 遍历上下左右四个方向
for (int a = 0; a < 4; a++) {
int newX = x + offsets[a][0];
int newY = y + offsets[a][1];
// 检查新位置是否合法且未被访问过,且不是墙
if (newX >= 0 && newX < n && newY >= 0 && newY < m && !visited[newX][newY] && matrix[newX][newY] != '#') {
// 标记为已访问
visited[newX][newY] = 1;
// 如果是敌人,敌人数加1
if (matrix[newX][newY] == 'E') count++;
// 将新位置加入队列
queue[rear][0] = newX;
queue[rear][1] = newY;
rear++;
}
}
}
return count;
}
// 主逻辑函数
int getResult() {
int ans = 0; // 符合条件的区域数量
// 遍历地图的每个点
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果该点未被访问过且不是墙,则进行BFS
if (!visited[i][j] && matrix[i][j] != '#') {
int enemyCount = bfs(i, j); // 获取当前区域的敌人数
if (enemyCount < k) ans++; // 如果敌人数小于k,则符合条件
}
}
}
return ans;
}
int main() {
// 读取输入
scanf("%d %d %d", &n, &m, &k);
for (int i = 0; i < n; i++) {
scanf("%s", matrix[i]);
}
// 输出结果
printf("%d\n", getResult());
return 0;
}
深度优先搜索(DFS)
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 101
// 地图行数, 地图列数, 区域敌军人数上限值
int n, m, k;
// 地图矩阵
char matrix[MAX_SIZE][MAX_SIZE] = {'\0'};
// 访问矩阵
int visited[MAX_SIZE][MAX_SIZE] = {0};
// 上下左右的方位偏移量
int offsets[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// DFS函数,返回当前区域的敌人数
int dfs(int i, int j) {
int count = 0; // 当前区域的敌人数
int stack[MAX_SIZE * MAX_SIZE][2]; // 栈,用于DFS
int top = 0;
// 标记起点为已访问
visited[i][j] = 1;
// 如果起点是敌人,敌人数加1
if (matrix[i][j] == 'E') count++;
// 将起点加入栈
stack[top][0] = i;
stack[top][1] = j;
top++;
// DFS遍历
while (top > 0) {
int x = stack[top - 1][0];
int y = stack[top - 1][1];
top--;
// 遍历上下左右四个方向
for (int a = 0; a < 4; a++) {
int newX = x + offsets[a][0];
int newY = y + offsets[a][1];
// 检查新位置是否合法且未被访问过,且不是墙
if (newX >= 0 && newX < n && newY >= 0 && newY < m && !visited[newX][newY] && matrix[newX][newY] != '#') {
// 标记为已访问
visited[newX][newY] = 1;
// 如果是敌人,敌人数加1
if (matrix[newX][newY] == 'E') count++;
// 将新位置加入栈
stack[top][0] = newX;
stack[top][1] = newY;
top++;
}
}
}
return count;
}
// 主逻辑函数
int getResult() {
int ans = 0; // 符合条件的区域数量
// 遍历地图的每个点
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果该点未被访问过且不是墙,则进行DFS
if (!visited[i][j] && matrix[i][j] != '#') {
int enemyCount = dfs(i, j); // 获取当前区域的敌人数
if (enemyCount < k) ans++; // 如果敌人数小于k,则符合条件
}
}
}
return ans;
}
int main() {
// 读取输入
scanf("%d %d %d", &n, &m, &k);
for (int i = 0; i < n; i++) {
scanf("%s", matrix[i]);
}
// 输出结果
printf("%d\n", getResult());
return 0;
}
代码讲解
核心逻辑
-
输入处理:
- 读取地图大小
n
、m
和敌人数上限k
。 - 读取地图矩阵
matrix
。
- 读取地图大小
-
搜索算法:
- BFS:使用队列实现,按层次遍历区域。
- DFS:使用栈实现,递归遍历区域。
-
区域统计:
- 遍历地图的每个点,如果该点是空地
.
或敌人E
,并且未被访问过,则从该点开始搜索整个区域。 - 在搜索过程中,统计区域内的敌人数量。
- 遍历地图的每个点,如果该点是空地
-
判断条件:
- 如果当前区域的敌人数小于
k
,则符合条件的区域数量加1
。
- 如果当前区域的敌人数小于
-
输出结果:
- 输出符合条件的区域数量。
以下是针对广度优先搜索(BFS)、深度优先搜索(DFS - 基于栈实现)、深度优先搜索(DFS - 基于递归实现)的表格,涵盖它们的核心特点、实现细节、适用场景和代码逻辑:
算法类型 | 广度优先搜索(BFS) | 深度优先搜索(DFS - 栈实现) | 深度优先搜索(DFS - 递归实现) |
---|---|---|---|
核心数据结构 | 队列(FIFO,先进先出) | 栈(LIFO,后进先出) | 函数调用栈(递归实现,隐式栈) |
遍历顺序 | 按层级逐层遍历,从起始节点开始,逐步推进至更远的节点 | 沿某一条路径深入到底,再回溯,继续寻找其他路径 | 沿某一条路径递归深入到底,再回溯,继续寻找其他路径 |
实现方式 | 显式使用 queue 队列记录待访问节点 | 显式使用 stack 栈记录待访问节点 | 隐式依赖函数调用栈 |
代码复杂度 | 使用队列实现,代码稍复杂,需手动管理队列 | 使用栈实现,代码稍复杂,需手动管理栈 | 代码简洁,直接递归调用,但需考虑递归深度 |
时间复杂度 | (O(N \times M)) | (O(N \times M)) | (O(N \times M)) |
空间复杂度 | 最坏情况下 (O(N \times M)),取决于队列大小 | 最坏情况下 (O(N \times M)),取决于栈大小 | 最坏情况下 (O(N \times M)),取决于递归深度 |
适用场景 | - 计算最短路径(例如迷宫问题) - 图的分层遍历 | - 连通区域搜索 - 图的全路径遍历 | - 小规模连通区域搜索 - 适合递归结构 |
优点 | - 能找到最短路径 - 层次结构清晰 | - 无递归限制 - 适合需要手动维护的场景 | - 代码简洁 - 自然适用于递归结构 |
缺点 | - 空间占用高(队列可能很大) | - 栈可能消耗内存,但比递归稳定 | - 递归深度过大可能导致栈溢出 |
实现逻辑 | 1. 初始化队列并将起点加入队列 2. 按层次依次访问节点 | 1. 初始化栈并将起点加入栈 2. 按路径深度访问节点 | 1. 递归处理当前节点 2. 对子节点递归调用 |
代码调用方式 | bfs(i, j) | dfs(i, j) | dfs(i, j, count) |
适用地图特点 | - 需要层次遍历或最短路径的网格 | - 需要全路径搜索或连通区域搜索的网格 | - 适合规模较小的网格,递归深度不大的场景 |
表格核心解释
-
核心数据结构:
- BFS 依赖外部显式的队列(FIFO),实现逐层遍历。
- DFS(栈实现)依赖外部显式的栈(LIFO),实现路径深度优先遍历。
- DFS(递归实现)依赖隐式的函数调用栈,递归处理节点。
-
遍历顺序:
- BFS 按照距离起点的层次逐层扩展。
- DFS(栈实现)和 DFS(递归实现)优先探索一条路径到底,再回溯探索其他路径。
-
时间与空间复杂度:
- 三种方法时间复杂度都是 (O(N \times M)),因为每个节点最多访问一次。
- 空间复杂度在最坏情况下都要存储所有节点((O(N \times M))),但 BFS 使用队列,DFS 使用栈或递归调用栈。
-
优缺点:
- BFS 更适合找到最短路径,但空间开销大。
- DFS(栈实现)和 DFS(递归实现)更适合全路径搜索,递归实现代码简洁,但需注意递归深度。
-
适用场景:
- BFS 更适合迷宫最短路径问题。
- DFS 更适合连通区域搜索和全路径问题,递归实现适合小规模问题。
总结
- BFS 和 DFS 都可以解决该问题,但 BFS 更适合大规模数据,避免栈溢出问题。
- 代码通过遍历地图的每个点,使用搜索算法统计区域内的敌人数量,并判断是否符合条件。
- 希望这个表格帮助您更清晰地理解这三种算法的特点和适用场景!如有疑问,欢迎继续讨论!
六、尾言
什么是华为OD?
华为OD(Outsourcing Developer,外包开发工程师)是华为针对软件开发工程师岗位的一种招聘形式,主要包括笔试、技术面试以及综合面试等环节。尤其在笔试部分,算法题的机试至关重要。
为什么刷题很重要?
-
机试是进入技术面的第一关:
华为OD机试(常被称为机考)主要考察算法和编程能力。只有通过机试,才能进入后续的技术面试环节。 -
技术面试需要手撕代码:
技术一面和二面通常会涉及现场编写代码或算法题。面试官会注重考察候选人的思路清晰度、代码规范性以及解决问题的能力。因此提前刷题、多练习是通过面试的重要保障。 -
入职后的可信考试:
入职华为后,还需要通过“可信考试”。可信考试分为三个等级:- 入门级:主要考察基础算法与编程能力。
- 工作级:更贴近实际业务需求,可能涉及复杂的算法或与工作内容相关的场景题目。
- 专业级:最高等级,考察深层次的算法以及优化能力,与薪资直接挂钩。
刷题策略与说明:
2024年8月14日之后,华为OD机试的题库转为 E卷,由往年题库(D卷、A卷、B卷、C卷)和全新题目组成。刷题时可以参考以下策略:
-
关注历年真题:
- 题库中的旧题占比较大,建议优先刷历年的A卷、B卷、C卷、D卷题目。
- 对于每道题目,建议深度理解其解题思路、代码实现,以及相关算法的适用场景。
-
适应新题目:
- E卷中包含全新题目,需要掌握全面的算法知识和一定的灵活应对能力。
- 建议关注新的刷题平台或交流群,获取最新题目的解析和动态。
-
掌握常见算法:
华为OD考试通常涉及以下算法和数据结构:- 排序算法(快速排序、归并排序等)
- 动态规划(背包问题、最长公共子序列等)
- 贪心算法
- 栈、队列、链表的操作
- 图论(最短路径、最小生成树等)
- 滑动窗口、双指针算法
-
保持编程规范:
- 注重代码的可读性和注释的清晰度。
- 熟练使用常见编程语言,如C++、Java、Python等。
如何获取资源?
-
官方参考:
- 华为招聘官网或相关的招聘平台会有一些参考信息。
- 华为OD的相关公众号可能也会发布相关的刷题资料或学习资源。
-
加入刷题社区:
- 找到可信的刷题交流群,与其他备考的小伙伴交流经验。
- 关注知名的刷题网站,如LeetCode、牛客网等,这些平台上有许多华为OD的历年真题和解析。
-
寻找系统性的教程:
- 学习一本经典的算法书籍,例如《算法导论》《剑指Offer》《编程之美》等。
- 完成系统的学习课程,例如数据结构与算法的在线课程。
积极心态与持续努力:
刷题的过程可能会比较枯燥,但它能够显著提升编程能力和算法思维。无论是为了通过华为OD的招聘考试,还是为了未来的职业发展,这些积累都会成为重要的财富。
考试注意细节
-
本地编写代码
- 在本地 IDE(如 VS Code、PyCharm 等)上编写、保存和调试代码,确保逻辑正确后再复制粘贴到考试页面。这样可以减少语法错误,提高代码准确性。
-
调整心态,保持冷静
- 遇到提示不足或实现不确定的问题时,不必慌张,可以采用更简单或更有把握的方法替代,确保思路清晰。
-
输入输出完整性
- 注意训练和考试时都需要编写完整的输入输出代码,尤其是和题目示例保持一致。完成代码后务必及时调试,确保功能符合要求。
-
快捷键使用
- 删除行可用
Ctrl+D
,复制、粘贴和撤销分别为Ctrl+C
,Ctrl+V
,Ctrl+Z
,这些可以正常使用。 - 避免使用
Ctrl+S
,以免触发浏览器的保存功能。
- 删除行可用
-
浏览器要求
- 使用最新版的 Google Chrome 浏览器完成考试,确保摄像头开启并正常工作。考试期间不要切换到其他网站,以免影响考试成绩。
-
交卷相关
- 答题前,务必仔细查看题目示例,避免遗漏要求。
- 每完成一道题后,点击【保存并调试】按钮,多次保存和调试是允许的,系统会记录得分最高的一次结果。完成所有题目后,点击【提交本题型】按钮。
- 确保在考试结束前提交试卷,避免因未保存或调试失误而丢分。
-
时间和分数安排
- 总时间:150 分钟;总分:400 分。
- 试卷结构:2 道一星难度题(每题 100 分),1 道二星难度题(200 分)。及格分为 150 分。合理分配时间,优先完成自己擅长的题目。
-
考试环境准备
- 考试前请备好草稿纸和笔。考试中尽量避免离开座位,确保监控画面正常。
- 如需上厕所,请提前规划好时间以减少中途离开监控的可能性。
-
技术问题处理
- 如果考试中遇到断电、断网、死机等技术问题,可以关闭浏览器并重新打开试卷链接继续作答。
- 出现其他问题,请第一时间联系 HR 或监考人员进行反馈。
祝你考试顺利,取得理想成绩!