LeetCode 874中等数组 · 哈希
模拟行走机器人 图解题解
这道题到底在问什么
机器人在无限大网格上从 [0,0] 出发,面朝北。指令 -2 表示左转 90 度,-1 表示右转 90 度,正数 x 表示朝当前方向前进 x 步。前进时若下一格是障碍就停在原地、转去执行下一条指令。返回走过的离原点最大欧氏距离的平方。
- 输入
- commands=[4,-1,3], obstacles=[]
- 输出
- 25 (走到 [3,4],3²+4²=25)
- 输入
- commands=[4,-1,4,-2,4], obstacles=[[2,4]]
- 输出
- 65 (被障碍挡后绕到 [1,8])
最优解:一步一步想明白
- 3记住这套「转向改下标、前进逐格查障碍、每步刷新最远距离平方」,下面每一帧都在套它。
- 4开局:机器人在原点 [0,0](紫色箭头朝北 ↑),红色 ▦ 是障碍 [2,3]。最远距离平方 ans 先记 0,下面开始读指令。
- 5先认舞台:横向是 x(向右增大、即东,◎ 标的就是右边一格),纵向是 y(向上增大、即北)。四个朝向按「北 东 南 西」排成一圈,机器人现在朝北,右转顺时针挪一格、左转逆时针挪一格。
- 6第 1 条指令是 3,朝 北 ↑ 前进 3 步,一步步走。第 1 步:先探前方一格 [0,1](◎ 标记),查它在不在障碍集合里,再决定走不走。
- 7前方没障碍,真走过去,到达 [0,1]。算一下 dist² = 0² + 1² = 1,比旧的最远还大,刷新 ans = 1。
- 8第 2 步:先探前方一格 [0,2](◎ 标记),查它在不在障碍集合里,再决定走不走。
- 9前方没障碍,真走过去,到达 [0,2]。算一下 dist² = 0² + 2² = 4,比旧的最远还大,刷新 ans = 4。
- 10第 3 步:先探前方一格 [0,3](◎ 标记),查它在不在障碍集合里,再决定走不走。
- 11前方没障碍,真走过去,到达 [0,3]。算一下 dist² = 0² + 3² = 9,比旧的最远还大,刷新 ans = 9。
- 12第 2 条指令是 -1,右转 90 度。朝向从 北 ↑ 转到 东 →(箭头跟着转),下标 k 往后挪一格。机器人原地不动,ans 仍是 9。
- 13第 3 条指令是 2,朝 东 → 前进 2 步,一步步走。第 1 步:先探前方一格 [1,3](◎ 标记),查它在不在障碍集合里,再决定走不走。
- 14前方没障碍,真走过去,到达 [1,3]。算一下 dist² = 1² + 3² = 10,比旧的最远还大,刷新 ans = 10。
- 15第 2 步:先探前方一格 [2,3](◎ 标记),查它在不在障碍集合里,再决定走不走。
- 16前方 [2,3] 是障碍 ▦,走不过去。机器人停在 [1,3] 不动,这条指令剩下的步数全部作废,直接去下一条指令。ans 保持 10。
- 17第 4 条指令是 -2,左转 90 度。朝向从 东 → 转到 北 ↑(箭头跟着转),下标 k 往回挪一格(加 3 再对 4 取模)。机器人没移动,ans 还是 10。
- 18第 5 条指令是 1,朝 北 ↑ 前进 1 步,一步步走。第 1 步:先探前方一格 [1,4](◎ 标记),查它在不在障碍集合里,再决定走不走。
- 19前方没障碍,真走过去,到达 [1,4]。算一下 dist² = 1² + 4² = 17,比旧的最远还大,刷新 ans = 17。
- 20全程走完,机器人最后停在 [1,4]。一路上离原点最远的那一刻就在这里,dist² = 1² + 4² = 17,这就是答案 17。
⚠️ 容易写错的地方
✗ 错:走完整条前进指令才更新最远距离
✓ 对:每走一步就用 x²+y² 刷新 ans
被障碍中途挡停时,最远点可能恰好在半路那一步,逐步更新才不漏
✗ 错:把 -2 当右转、-1 当左转
✓ 对:-2 是左转、-1 是右转
方向反了整条路径全错,下标加减要对应正确
✗ 错:撞障碍后放弃后面所有指令
✓ 对:只是停在前一格,继续执行下一条指令
机器人会绕障碍接着走,后面可能到更远的点
完整代码(Python / C++ / Java)
Python
from __future__ import annotations
from typing import *
from collections import *
from functools import *
from itertools import *
from math import *
from heapq import *
from bisect import *
class Solution:
def robotSim(self, commands: List[int], obstacles: List[List[int]]) -> int:
dirs = (0, 1, 0, -1, 0)
s = {(x, y) for x, y in obstacles}
ans = k = 0
x = y = 0
for c in commands:
if c == -2:
k = (k + 3) % 4
elif c == -1:
k = (k + 1) % 4
else:
for _ in range(c):
nx, ny = x + dirs[k], y + dirs[k + 1]
if (nx, ny) in s:
break
x, y = nx, ny
ans = max(ans, x * x + y * y)
return ansC++
#include <algorithm>
#include <array>
#include <cctype>
#include <climits>
#include <cmath>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
int robotSim(vector<int>& commands, vector<vector<int>>& obstacles) {
int dirs[5] = {0, 1, 0, -1, 0};
auto f = [](int x, int y) {
return x * 60010 + y;
};
unordered_set<int> s;
for (auto& e : obstacles) {
s.insert(f(e[0], e[1]));
}
int ans = 0, k = 0;
int x = 0, y = 0;
for (int c : commands) {
if (c == -2) {
k = (k + 3) % 4;
} else if (c == -1) {
k = (k + 1) % 4;
} else {
while (c--) {
int nx = x + dirs[k], ny = y + dirs[k + 1];
if (s.count(f(nx, ny))) {
break;
}
x = nx;
y = ny;
ans = max(ans, x * x + y * y);
}
}
}
return ans;
}
};Java
import java.util.*;
class Solution {
public int robotSim(int[] commands, int[][] obstacles) {
int[] dirs = {0, 1, 0, -1, 0};
Set<Integer> s = new HashSet<>(obstacles.length);
for (int[] e : obstacles) {
s.add(f(e[0], e[1]));
}
int ans = 0, k = 0;
int x = 0, y = 0;
for (int c : commands) {
if (c == -2) {
k = (k + 3) % 4;
} else if (c == -1) {
k = (k + 1) % 4;
} else {
while (c-- > 0) {
int nx = x + dirs[k], ny = y + dirs[k + 1];
if (s.contains(f(nx, ny))) {
break;
}
x = nx;
y = ny;
ans = Math.max(ans, x * x + y * y);
}
}
}
return ans;
}
private int f(int x, int y) {
return x * 60010 + y;
}
}复杂度
时间
O(n + k)
n 条指令依次执行,前进总步数加起来记 k,每步查障碍 O(1)
空间
O(m)
m 个障碍存进哈希集合,方便 O(1) 判断某格是不是障碍
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 模拟行走机器人 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么用方向数组,而不是写四个 if-else 判断东南西北?+
方向数组把「转向」变成下标的加减取模,左右转各一行就搞定,代码短又不容易写错;以后要扩成八个方向也只需把数组和取模的底数改大,扩展性比一堆 if-else 好很多。
障碍可能有上万个,怎么快速判断某一格是不是障碍?+
把障碍坐标全放进哈希集合,查询就是 O(1)。坐标可以像参考代码那样编码成一个整数(x*60010+y),也可以直接用 (x,y) 这样的元组或 pair 作为集合元素,效果一样。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 模拟行走机器人 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。