LeetCode: 529. Minesweeper

LeetCode: 529. Minesweeper

题目基本是写一个简要版的Minesweeper,给出了三个规则:

  • 如果点到一个隐藏的地雷M,把它改为X,游戏结束
  • 如果点到一个E,且其周围8邻接的范围没有地雷,那么应该把8邻接的范围的格子全部翻开为E
  • 如果翻开的格子的八邻接范围有隐藏的地雷,就将其标注了地雷的数目1-8而非E

一开始感觉题目还是有没说清楚的地方,就是原题里只提到邻接范围,而没有仔细说明是4邻接还是8邻接。后来仔细想想,扫雷都是指一个九宫格里的地雷数(比较少玩,所以一开始没想起来……),所以是8邻域。
至于解法,其实题目条件2已经暴露了–recursively,递归搜索,就是BFS或者DFS了。其中BFS比较好理解(参考二叉树的层次遍历)。

BFS

用deque是因为可以实现双向插入删除,其实就BFS而言,是一个先进先出的队列,用list也是没有问题的。

时间复杂度是O(n*m),空间复杂度是O(n*m)。

vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click)
{
    int column = click[0];
    int row = click[1];
    deque<pair<int,int>> EToBeRevealed;

    EToBeRevealed.clear();

    //1.
    if(board[column][row] == 'M')
        board[column][row] = 'X';
    else if(board[column][row] == 'E')
    {
        EToBeRevealed.push_back({column,row});
        while(!EToBeRevealed.empty())
        {
            pair<int,int> reveal = EToBeRevealed.front();
            int tc = reveal.first;
            int tr = reveal.second;
            EToBeRevealed.pop_front();
            if(board[tc][tr] == 'B')
                continue;
            board[tc][tr] = 'B';

            //number of 'M' around
            int numOfUnrevealedM = 0;
            vector<pair<int,int>> toBePush;
            for(int pc = -1;pc <= 1;pc++)
            {
                for(int pr = -1;pr <= 1;pr++)
                {
                    if(tc+pc >= 0&& tc+pc < board.size() && tr+pr >= 0 && tr+pr < board[0].size())
                    {
                        if(board[tc+pc][tr+pr] == 'E')
                            toBePush.push_back({tc+pc,tr+pr});
                        if(board[tc+pc][tr+pr] == 'M')
                        {
                            numOfUnrevealedM++;
                        }
                    }


                }
            }
            //3.
            if(numOfUnrevealedM)
                board[tc][tr] = '0' + numOfUnrevealedM;
            //find 'E' around
            else EToBeRevealed.insert(EToBeRevealed.end(),toBePush.begin(),toBePush.end());
        }
    }
    return board;
}

要注意要剔除visited的格子,一个格子能被加入EToBeRevealed中很多次,但是通过检查是否visited(是否为B)保证只执行一次,不然队列会变得非常大,超出时间的限制。

各种循环的判定和操作都要小心。vector的初始化可以用{,,,}。

还有DFS版本的,用的递归。个人不是很推荐,容易出错,虽然代码会简洁些。

DFS

vector<pair<int,int>> link = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
vector<char> mines = {'B', '1','2','3','4','5','6','7','8'};
vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) {
    int m = board.size();
    int n = board[0].size();
    if(board[click[0]][click[1]] == 'M') {board[click[0]][click[1]] = 'X'; return board;}
    dfs(board, click[0], click[1], m, n);
    return board;
}

void dfs(vector<vector<char>>& board, int i, int j, int m, int n){
    if(i < 0 || i >= m || j < 0 || j >= n) return;
    if(board[i][j] == 'E'){
        int count = 0;
        for(auto d : link){
            int tmpi = i + d.first;
            int tmpj = j + d.second;
            if(tmpi >= 0 && tmpi < m && tmpj >= 0 && tmpj < n){
                if(board[tmpi][tmpj] == 'M') count++;
            }
        }
        board[i][j] = mines[count];
        if(board[i][j] == 'B'){
            for(auto lk : link){
                dfs(board, i+lk.first, j+lk.second, m, n);
            }
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
给定一个整数数组 nums 和一个目标值 target,要求在数组中找出两个数的和等于目标值,并返回这两个数的索引。 思路1:暴力法 最简单的思路是使用两层循环遍历数组的所有组合,判断两个数的和是否等于目标值。如果等于目标值,则返回这两个数的索引。 此方法的时间复杂度为O(n^2),空间复杂度为O(1)。 思路2:哈希表 为了优化时间复杂度,可以使用哈希表来存储数组中的元素和对应的索引。遍历数组,对于每个元素nums[i],我们可以通过计算target - nums[i]的值,查找哈希表中是否存在这个差值。 如果存在,则说明找到了两个数的和等于目标值,返回它们的索引。如果不存在,将当前元素nums[i]和它的索引存入哈希表中。 此方法的时间复杂度为O(n),空间复杂度为O(n)。 思路3:双指针 如果数组已经排序,可以使用双指针的方法来求解。假设数组从小到大排序,定义左指针left指向数组的第一个元素,右指针right指向数组的最后一个元素。 如果当前两个指针指向的数的和等于目标值,则返回它们的索引。如果和小于目标值,则将左指针右移一位,使得和增大;如果和大于目标值,则将右指针左移一位,使得和减小。 继续移动指针,直到找到两个数的和等于目标值或者左指针超过了右指针。 此方法的时间复杂度为O(nlogn),空间复杂度为O(1)。 以上三种方法都可以解决问题,选择合适的方法取决于具体的应用场景和要求。如果数组规模较小并且不需要考虑额外的空间使用,则暴力法是最简单的方法。如果数组较大或者需要优化时间复杂度,则哈希表或双指针方法更合适。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值