5.6
59. 螺旋矩阵 II - 力扣(LeetCode)
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int dirs[4][2] = {{0 , 1} ,{1 , 0} , {0 , -1} , {-1 , 0}};
vector<vector<int>> res(n , vector<int>(n , 0));
for(int k = 1 , x = 0 , y = 0, d = 0 ; k <= n * n ; k ++){
res[x][y] = k;
int nextx = x + dirs[d][0];
int nexty = y + dirs[d][1];
if(nextx < 0 || nextx >= n || nexty < 0 || nexty >= n || res[nextx][nexty]){
d = (d + 1) % 4;
}
x += dirs[d][0];
y += dirs[d][1];
}
return res;
}
};
复杂度分析
- 时间复杂度:O(n2)。
- 空间复杂度:O(1)。返回值不计入。
相似题目
- 1041. 困于环中的机器人 1521
- 874. 模拟行走机器人 1846
- 2069. 模拟行走机器人 II 1919
1041. 困于环中的机器人 - 力扣(LeetCode)
当机器人执行完指令 instructions 后,它的位置和方向均有可能发生变化。
如果它的位置仍位于原点,那么不管它此时方向是什么,机器人都将永远无法离开。
如果它的位置不在原点,那么需要考虑此时机器人的方向:
- 如果机器人仍然朝北,那么机器人可以不会陷入循环。假设执行完一串指令后,机器人的位置是 (x,y) 且不为原点,方向仍然朝北,那么执行完第二串指令后,机器人的位置便成为 (2×x,2×y),会不停地往外部移动,不会陷入循环。
- 如果机器人朝南,那么执行第二串指令时,机器人的位移会与第一次相反,即第二次的位移是 (−x,−y),并且结束后会回到原来的方向。这样一来,每两串指令之后,机器人都会回到原点,并且方向朝北,机器人会陷入循环。
- 如果机器人朝东,即右转了 90°。这样一来,每执行一串指令,机器人都会右转 90°。那么第一次和第三次指令的方向是相反的,第二次和第四次指令的方向是相反的,位移之和也为 0,这样一来,每四次指令之后,机器人都会回到原点,并且方向朝北,机器人会陷入循环。如果机器人朝西,也是一样的结果。
因此,机器人在循环中 == 循环后回到原点 || 不在原点但是方向不向北
class Solution {
public:
bool isRobotBounded(string instructions) {
int dirs[4][2] = {{0 , 1} ,{1 , 0} , {0 , -1} , {-1 , 0}};//表示坐标(x , y)
int d = 0;//向北
int x = 0 , y = 0;
for(char c : instructions){
if(c == 'L') d= (d + 3) % 4;
else if(c == 'R') d =(d + 1) % 4;
else{
x += dirs[d][0];
y += dirs[d][1];
}
}
return (x == 0 && y == 0 ) || (d != 0);
}
};
874. 模拟行走机器人 - 力扣(LeetCode)
方法:哈希表
思路与算法
题目给出一个在点 (0,0) ,并面向北方的机器人。现在有一个大小为 n 的命令数组 commands 来操作机器人的移动,和一个大小为 m 的障碍物数组 obstacles。现在我们通过 commands 来模拟机器人的移动,并用一个哈希表来存储每一个障碍物放置点。当机器人的指令为向前移动时,我们尝试往前移动对应的次数——若往前一个单位不为障碍物放置点(即不在哈希表中),则机器人向前移动一个单位,否则机器人保持原位不变。
在机器人移动的过程中记录从原点到机器人所有经过的整数路径点的最大欧式距离的平方即为最后的答案。
st初始化方式
原代码试图用obstacles的每个vector
元素初始化st,但unordered_set的元素类型是int,而每个障碍物是一个二维点,这里应该将每个点转换为一个唯一的整数键。比如,可以将每个坐标对(x,y)转换为一个字符串,或者像其他题解中常见的,用x左移或者乘以一个大数加上y,形成唯一的整数。例如,每个障碍物坐标可以存为x * 60000 + y,假设x和y的范围在题目中不会超过这个数。 补充:contains()
C++ 中的
find和contains都是用来查找容器中是否包含指定元素的方法,但它们的用法和功能略有不同。
find方法:
find方法用于在容器中查找指定元素,如果找到则返回指向该元素的迭代器,如果找不到则返回指向容器末尾的迭代器。
find方法适用于大多数标准库容器,如std::vector,std::set,std::map等。用法示例:
std::vector<int> vec = {1, 2, 3, 4, 5}; auto it = std::find(vec.begin(), vec.end(), 3); if (it != vec.end()) { std::cout << "Element found at index: " << std::distance(vec.begin(), it) << std::endl; } else { std::cout << "Element not found" << std::endl; }
contains方法:
contains方法是 C++20 中引入的新方法,用于检查容器是否包含指定元素,返回一个bool值表示是否找到了该元素。
contains方法适用于支持 C++20 标准的容器,如std::vector,std::set,std::map等。用法示例:
std::vector<int> vec = {1, 2, 3, 4, 5}; if (std::ranges::contains(vec, 3)) { std::cout << "Element found" << std::endl; } else { std::cout << "Element not found" << std::endl; }总之,
find方法返回一个迭代器,contains方法返回一个布尔值。在使用时应根据具体的需求选择合适的方法。
contains相关用法说明C++ 标准库:
unordered_set::count(key):返回键的存在次数(0或1),推荐用于C++20之前。
unordered_set::contains(key)(C++20起):直接返回布尔值,更直观。
ranges::contains:属于C++20 ranges库,用于检查范围中是否存在元素,例如:vector<int> v = {1, 2, 3}; bool hasTwo = ranges::contains(v, 2);但不适用于直接检查集合中的键。
class Solution {
public:
int robotSim(vector<int>& commands, vector<vector<int>>& obstacles) {
unordered_set<int> uset;
for(auto& obstacle : obstacles){
uset.emplace(obstacle[0] * 60000 + obstacle[1]);
}
int dirs[4][2] = {{0 , 1} , {1 , 0}, {0 , -1} , {-1 , 0}};
int d = 0;//初始向北
int x = 0 , y = 0;//初始坐标
int res = 0;
for(int i : commands){
if(i == -2) d = (d + 3) % 4;
if(i == -1) d = (d + 1) % 4;
else{
for(int k = 0 ; k < i ; k ++){
int nextx = x + dirs[d][0];
int nexty = y + dirs[d][1];
if((uset.count(nextx * 60000 + nexty))) break;
x += dirs[d][0];
y += dirs[d][1];
res = max(res , x * x + y * y);
}
}
}
return res;
}
};
本题中改为: ranges方法超时了。
if((ranges::contains(uset , nextx * 60000 + nexty))) break;conut改为contains可以:
if((uset.contains(nextx * 60000 + nexty))) break;
2069. 模拟行走机器人 II - 力扣(LeetCode)
用常规方法是超时的:
class Robot {
public:
int dirs[4][2] = {{0 , 1} , {1 , 0}, {0 , -1} , {-1 , 0}};
int d = 1;//初始向东
int x = 0 , y = 0;//初始坐标
int w , h;
Robot(int width, int height) {
w = width;
h = height;
}
void step(int num) {
for(int i = 0 ; i < num ; i ++){
int nextx = x + dirs[d][0];
int nexty = y + dirs[d][1];
if(nextx < 0 || nextx >= w || nexty < 0 || nexty >= h) d = (d + 3) % 4;
x += dirs[d][0];
y += dirs[d][1];
}
}
vector<int> getPos() {
return {x , y};
}
string getDir() {
if(d == 1) return "East";
else if(d == 2) return "South";
else if(d == 3) return "West";
else return "North";
}
};
/**
* Your Robot object will be instantiated and called as such:
* Robot* obj = new Robot(width, height);
* obj->step(num);
* vector<int> param_2 = obj->getPos();
* string param_3 = obj->getDir();
*/
方法:模拟
思路与算法
根据题目描述,我们可以发现:机器人总是会在网格的「最外圈」进行循环移动。(此图的四个角画错了)
因此,我们可以将机器人移动的循环节(位置以及移动方向)预处理出来,存储在数组中,并用一个指针 idx 指向数组中的某个位置,表示当前机器人的位置以及移动方向。
预处理可以分为四个步骤完成。如上图所示,不同颜色的网格表示机器人在对应网格上的不同方向,因此我们可以使用四个循环分别枚举每一种颜色对应的网格的位置,把它们加入预处理的数组即可。
对于题目要求实现的三个接口,我们可以依次实现:
void step(int num):我们可以将 idx 的值增加 num。由于机器人的移动路径是循环的,我们需要将增加后的值对循环的长度取模。
int[] getPos():我们根据 idx 返回预处理数组中的位置即可。
String getDir():我们根据 idx 返回预处理数组中的朝向即可。
细节
需要注意的是。当机器人回到原点时,它的朝向为「南」,但机器人初始在原点时的朝向为「东」。因此我们可以将预处理数组中原点的朝向改为「南」,并使用一个布尔变量记录机器人是否移动过:
如果机器人未移动过,我们总是返回「东」朝向;
如果机器人移动过,我们根据 idx 返回预处理数组中的朝向。
代码
class Robot { private: bool moved = false; int idx = 0; vector<pair<int, int>> pos; vector<int> dir; unordered_map<int, string> to_dir = { {0, "East"}, {1, "North"}, {2, "West"}, {3, "South"} }; public: Robot(int width, int height) { //走在哪条边上的方向是固定值,可以预处理 for (int i = 0; i < width; ++i) { pos.emplace_back(i, 0); dir.emplace_back(0);//底边 ,东 } for (int i = 1; i < height; ++i) {//注意y从1开始,最右下角的点还是向东 pos.emplace_back(width - 1, i); dir.emplace_back(1);//右边,北 } for (int i = width - 2; i >= 0; --i) { pos.emplace_back(i, height - 1); dir.emplace_back(2);//上面,西 } for (int i = height - 2; i > 0; --i) { pos.emplace_back(0, i); dir.emplace_back(3);//左边,南 } dir[0] = 3;//初始规定向南 } void move(int num) { moved = true; idx = (idx + num) % pos.size(); } vector<int> getPos() { return {pos[idx].first, pos[idx].second}; } string getDir() { if (!moved) { return "East"; } return to_dir[dir[idx]]; } };复杂度分析
时间复杂度:预处理的时间复杂度为 O(width+height),所有查询接口的时间复杂度均为 O(1)。
空间复杂度:O(width+height),即为存储预处理数组需要的空间。
543. 二叉树的直径 - 力扣(LeetCode)
本题有两个关键概念:
- 链:从下面的某个节点(不一定是叶子)到当前节点的路径。把这条链的边数之和,作为 dfs 的返回值。
- 直径:等价于由两条(或者一条)链拼成的路径。我们枚举每个 node,假设直径在这里「拐弯」,也就是计算由左右两条从下面的某个节点(不一定是叶子)到 node 的链的边数之和,去更新答案的最大值。
class Solution {
public:
int diameterOfBinaryTree(TreeNode* root) {
int res = 0;
auto dfs = [&](this auto&& dfs , TreeNode* node)->int{
if(!node) return -1;
int left = dfs(node->left) + 1;
int right = dfs(node->right) + 1;
res = max(res , left + right);
return max(left , right);
};
dfs(root);
return res;
}
};




浙公网安备 33010602011771号