《程序员面试金典(第6版)》面试题 16.22. 兰顿蚂蚁(哈希映射,C++)

题目描述

一只蚂蚁坐在由白色和黑色方格构成的无限网格上。开始时,网格全白,蚂蚁面向右侧。每行走一步,蚂蚁执行以下操作。传送门

  • (1) 如果在白色方格上,则翻转方格的颜色,向右(顺时针)转 90 度,并向前移动一个单位。
  • (2) 如果在黑色方格上,则翻转方格的颜色,向左(逆时针方向)转 90 度,并向前移动一个单位。

编写程序来模拟蚂蚁执行的前 K 个动作,并返回最终的网格。

  • 网格由数组表示,每个元素是一个字符串,代表网格中的一行,黑色方格由 ‘X’ 表示,白色方格由 ‘_’ 表示,蚂蚁所在的位置由 ‘L’, ‘U’, ‘R’, ‘D’ 表示,分别表示蚂蚁 左、上、右、下 的朝向。只需要返回能够包含蚂蚁走过的所有方格的最小矩形。

示例 1:

输入: 0
输出: ["R"]

示例 2:

输入: 2
输出:
[
  "_X",
  "LX"
]

示例 3:

输入: 5
输出:
[
  "_U",
  "X_",
  "XX"
]

说明:

  • K <= 100000

解题思路与代码

emmmmm,这道题,是一道相当困难的题。困难的点在于题目的理解方面。我感觉,阅读理解不是顶级的选手,做这道题可能都得吃瘪。

首先,这道题,可不是简简单单的这句话而已只需要返回能够包含蚂蚁走过的所有方格的最小矩形。
就看那个返回值,你可能就会迷糊住,为啥输入2,返回的确实那样的呢?蚂蚁明明只走了2步,为啥有4个格子?是不是就会感到很疑惑?

在看到,蚂蚁在白格黑格上面反复跳跃,还正着反着转圈圈,我估计你就觉得更恶心把,咋会有人出这样的题,还不好好写题目说明,对吧。

其他方面倒是还好,只要彻底理解了题意,这道题也不算是特别困难的题,但也是一道中等难度偏上的题。因为除了基本的映射外,颜色的反转,不同的方向转圈,确实得够你吃一壶的。

那八皇后这道题来对比,题目难理解程度更高,题目复杂程度差不多,但我感觉也更高一些。

这里我再补充一下解题的思路:

  • 初始化网格:首先,我们需要创建一个足够大的二维网格,用来模拟蚂蚁的移动。这个网格应该足够大,以至于在蚂蚁的前K步移动中,它不会走出网格。每个单元格的初始值为’_',表示白色方格。我们还需要将蚂蚁的初始位置和朝向设定好。

  • 模拟蚂蚁的移动:对于前K步,我们需要根据蚂蚁当前所在的单元格的颜色和蚂蚁的朝向来决定下一步的移动。如果蚂蚁在白色方格上,我们需要把方格颜色翻转为黑色,然后蚂蚁向右转90度并向前移动一个单位。反之,如果蚂蚁在黑色方格上,我们需要把方格颜色翻转为白色,然后蚂蚁向左转90度并向前移动一个单位。注意,我们需要更新蚂蚁的朝向。

  • 处理网格扩展:由于蚂蚁的移动可能会超出当前网格的边界,我们需要在这种情况下动态地扩展网格。当蚂蚁即将走出网格时,我们可以添加新的行或列来扩展网格。

  • 提取结果:最后,我们需要找到包含蚂蚁走过的所有单元格的最小矩形,并返回这个矩形。这个矩形可以通过找到蚂蚁走过的单元格的最小和最大的行号和列号来确定。在提取结果时,我们需要把蚂蚁所在的位置和朝向也包含进去。

方法一 : 哈希法

接下来我就讲一下这道题基本的做法:

  • 这道题我们要创建很多的变量,来标记一些东西。首先是,蚂蚁走过的格子,我们也可以把它映射到一个无限延伸的棋盘上。
  • 那么如何去创建这个棋盘呢?首先普通的二维数组不能满足,因为棋盘不光有x,y坐标,还有它拥有的一种额外的属性,那便是颜色。蚂蚁走过的格子可是会变色的啊。
  • 那么怎么办呢?我们要创建一个嵌套的unordered_map<int, unordered_map<int, bool>> map;来表示这个棋盘,其中外层的键是y坐标,内层的键是x坐标,内层的值呢,就是棋盘格子的颜色啦,完美符合要求。
  • 之后,创建一个蚂蚁朝向的数组,代表蚂蚁的方向。char dirs[] = {'R','D','L','U'}; 之后再创建一个当前蚂蚁朝向的对应数组的下标变量int dir = 0; // 0是向右,1是向下,2是向左,3是向上
  • 接着定义蚂蚁的初始坐标int x = 0, y = 0,再定义两个数组,结合着dir,为的是去确定蚂蚁走后一步x,y是什么。
    • int dx[] = {1,0,-1,0}, dy[] = {0,1,0,-1};
  • 最后创建4个边界变量,这些边界变量的作用就是圈出棋盘的矩形出来。等会在while循环的遍历中,我们要不断的去更新这些边界变量。
    • int maxX = 0, maxY = 0, minX = 0, minY = 0;
  • 之后呢,我们使用一个while循环,去把蚂蚁走过的格子,颜色,等等,都遍历到那个嵌套map中去。你可以先想一下如何遍历,我们代码里会具体交代的。
  • 这样,我们就得到了一个蚂蚁走过的所有格子的动态棋盘。这样,其实我们就可以正式的准备最后的返回结果了。
  • 创建一个vector<string> result (maxY - minY + 1,string(maxX - minX + 1,'_'));并将返回结果的部分棋盘全部初始化为白色。至于result结果集为什么这样去初始化,我就不用再细细讲了吧。
  • 最后用双层for循环,再次将嵌套map的动态棋盘映射到数组中,但是注意,前面的动态棋盘x,y的值有正有负。但是在数组中,x,y的值就必须都是正的。所以我们要平移整个蚂蚁走过的区域。这也就是为什么我们当初要创建边界变量的原因。
  • 最后的最后,在把蚂蚁的朝向映射到返回数组中,我们就可以提交结果了。

具体的代码如下:

class Solution {
public:
    vector<string> printKMoves(int K) {
        unordered_map<int, unordered_map<int, bool>> map; // 定义一个嵌套哈希map,来存储坐标以及坐标对应的颜色
        int dir = 0; // 0是向右,1是向下,2是向左,3是向上
        int x = 0, y = 0; // 定义蚂蚁的初始坐标。
        char dirs[] = {'R','D','L','U'}; // 用来最后确定蚂蚁朝向的数组
        // dx,dy是用来帮助记录蚂蚁走后的坐标与朝向的。
        int dx[] = {1,0,-1,0}, dy[] = {0,1,0,-1};
        // 记录最大的,最小的x,y的坐标值
        int maxX = 0, maxY = 0, minX = 0, minY = 0;
        while(K--){ // 把蚂蚁走过的所有格子,都遍历进这个map中,并标记颜色
            // 先根据格子颜色,决定朝哪个方向走
            if(map[y][x]) dir = (dir + 3) % 4; 
            else dir = (dir + 1) % 4;
            // 再反转当前格子颜色
            map[y][x] = !map[y][x];
            // 之后再走一步
            x += dx[dir];
            y += dy[dir];
            // 最后更新边界信息
            minX = min(minX,x);
            minY = min(minY,y);
            maxX = max(x,maxX);
            maxY = max(y,maxY);
        }
        // 创建一个最后的结果集,由于最后的数组的坐标都是正的,而map中记录的坐标有正有负,所以要根据最左下角坐标去做整体图形的平移 
        // 所有点位偏移(minx,miny) 个距离。
        vector<string> result (maxY - minY + 1,string(maxX - minX + 1,'_'));
        for(auto& i : map)
            for(auto& j : i.second)
                if(j.second)
                    result[i.first - minY][j.first - minX] = 'X';
        // 由于最后蚂蚁停留在在 (x,y)上,但是这个点有正负,要返回偏移后的点。
        result[y - minY][x - minX] = dirs[dir];
        return result;
    }
};

在这里插入图片描述

复杂度分析

时间复杂度:O(K)

  • 时间复杂度主要由蚂蚁移动的步数 K 决定,每次移动的操作包括更新蚂蚁的方向、坐标以及对应格子的颜色,这些操作的时间复杂度都是常数时间 O(1)。另外,在蚂蚁完成所有移动后,还需要遍历一次 map 来构造最终的结果,这个过程的时间复杂度取决于蚂蚁访问过的格子数量,但由于蚂蚁最多只能移动 K 步,所以访问过的格子数量不会超过 K,因此这个过程的时间复杂度也是 O(K)。综上,整个代码的时间复杂度是 O(K)。

空间复杂度:O(K)

  • 空间复杂度主要由 map 的大小决定,map 中存储了蚂蚁访问过的所有格子的坐标以及对应的颜色,每次移动都可能访问到一个新的格子,所以 map 的大小最多为 K,因此空间复杂度是 O(K)。另外,最终的结果 result 占用的空间也取决于蚂蚁访问过的格子数量,但同样不会超过 K,所以这部分的空间复杂度也是 O(K)。综上,整个代码的空间复杂度是 O(K)。

这里假设 K 是蚂蚁的移动步数,如果 K 代表其他的量(例如格子的数量、方向的数量等),那么复杂度可能会不同。

总结

这道题目是一道模拟题,其主要目的是考察编程能力、逻辑思考能力以及对细节的把握。题目中蚂蚁在格子上行走的规则,以及最后需要返回的结果都具有一定的复杂性。这要求你能够准确地理解题意,找出解题的规律,并且把规律用代码实现出来。

具体到这道题,它考察了以下几个方面的能力:

  • 对数据结构的运用:这道题中需要用到嵌套的哈希表来记录蚂蚁走过的每个格子的颜色。

  • 对模拟问题的处理:你需要根据题目的描述,模拟出蚂蚁在格子上行走的过程。

  • 对边界的处理:你需要找出蚂蚁走过的格子中x坐标和y坐标的最大值和最小值,以此来确定最后返回结果的大小。

  • 对细节的把握:题目中蚂蚁在白色格子和黑色格子上的行为是不同的,这是解题的关键,需要仔细理解并准确实现。

所以,这道题的意义主要是考察你的编程能力、逻辑思考能力以及对细节的把握。

最后的最后,如果你觉得我的这篇文章写的不错的话,请给我一个赞与收藏,关注我,我会继续给大家带来更多更优质的干货内容

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿宋同学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值