【1222. 可以攻击国王的皇后】

来源:力扣(LeetCode)

描述:

在一个 8x8 的棋盘上,放置着若干「黑皇后」和一个「白国王」。

给定一个由整数坐标组成的数组 queens ,表示黑皇后的位置;以及一对坐标 king ,表示白国王的位置,返回所有可以攻击国王的皇后的坐标(任意顺序)。

示例 1:

【1222. 可以攻击国王的皇后】_第1张图片

输入:queens = [[0,1],[1,0],[4,0],[0,4],[3,3],[2,4]], king = [0,0]
输出:[[0,1],[1,0],[3,3]]
解释: 
[0,1] 的皇后可以攻击到国王,因为他们在同一行上。 
[1,0] 的皇后可以攻击到国王,因为他们在同一列上。 
[3,3] 的皇后可以攻击到国王,因为他们在同一条对角线上。 
[0,4] 的皇后无法攻击到国王,因为她被位于 [0,1] 的皇后挡住了。 
[4,0] 的皇后无法攻击到国王,因为她被位于 [1,0] 的皇后挡住了。 
[2,4] 的皇后无法攻击到国王,因为她和国王不在同一行//对角线上。

示例 2:

【1222. 可以攻击国王的皇后】_第2张图片

输入:queens = [[0,0],[1,1],[2,2],[3,4],[3,5],[4,4],[4,5]], king = [3,3]
输出:[[2,2],[3,4],[4,4]]

示例 3:

【1222. 可以攻击国王的皇后】_第3张图片

输入:queens = [[5,6],[7,7],[2,1],[0,7],[1,6],[5,1],[3,7],[0,3],[4,0],[1,2],[6,3],[5,0],[0,4],[2,2],[1,1],[6,4],[5,4],[0,0],[2,6],[4,5],[5,2],[1,4],[7,5],[2,3],[0,5],[4,2],[1,0],[2,7],[0,1],[4,6],[6,1],[0,6],[4,3],[1,7]], king = [3,4]
输出:[[2,3],[1,4],[1,6],[3,7],[4,3],[5,4],[4,5]]

提示:

  • 1 <= queens.length <= 63
  • queens[i].length == 2
  • 0 <= queens[i][j] < 8
  • king.length == 2
  • 0 <= king[0], king[1] < 8
  • 一个棋盘格上最多只能放置一枚棋子。

方法一:从国王出发

思路与算法

我们可以依次枚举八个方向,并从国王出发,其遇到的第一个皇后就可以攻击到它。

记国王的位置为 (kx, ky),枚举的方向为 (dx, dy),那么我们不断地将 kx 加上 dx,将 ky 加上 dy,直到遇到皇后或者走出边界位置。为了记录皇后的位置,我们可以使用一个 8 × 8 的二维数组,也可以使用一个哈希表,这样就可以在 O(1) 的时间内判断某一个位置是否有皇后。

代码:

class Solution {
public:
    vector<vector<int>> queensAttacktheKing(vector<vector<int>>& queens, vector<int>& king) {
        unordered_set<int> queen_pos;
        for (const auto& queen: queens) {
            int x = queen[0], y = queen[1];
            queen_pos.insert(x * 8 + y);
        }

        vector<vector<int>> ans;
        for (int dx = -1; dx <= 1; ++dx) {
            for (int dy = -1; dy <= 1; ++dy) {
                if (dx == 0 && dy == 0) {
                    continue;
                }
                int kx = king[0] + dx, ky = king[1] + dy;
                while (kx >= 0 && kx < 8 && ky >= 0 && ky < 8) {
                    int pos = kx * 8 + ky;
                    if (queen_pos.count(pos)) {
                        ans.push_back({kx, ky});
                        break;
                    }
                    kx += dx;
                    ky += dy;
                }
            }
        }
        return ans;
    }
};

时间 0ms 击败 100.00%使用 C++ 的用户
内存 10.96MB 击败 19.88%使用 C++ 的用户
复杂度分析

  • 时间复杂度:O(n+C),其中 n 是数组 queens 的长度,C 是棋盘的大小,在本题中 C = 8 。我们需要 O(n) 的时间将所有皇后放入哈希表中,后续的枚举部分一共有 8 个方向,每两个对称的方向最多会遍历 C 个位置,因此一共最多遍历 4C = O© 个位置。
  • 空间复杂度:O(n),即为哈希表需要使用的空间。

方法二:从皇后出发

思路与算法

我们枚举每个皇后,判断它是否在国王的八个方向上。如果在,说明皇后可以攻击到国王。

记国王的位置为 (kx, ky),皇后的位置为 (qx, qy),那么皇后相对于国王的位置为 (x, y) = (qx − kx, qy − ky),显然当 x = 0 或 y = 0 或 ∣x∣=∣y∣ 时,皇后可以攻击到国王,方向为 (sgn(x),sgn(y)),其中 sgn(x) 为符号函数,当 x > 0 时为 1,x < 0 时为 −1,x = 0 时为 0。

同一个方向的皇后可能有多个,我们需要选择距离国王最近的那一个,因此可以使用一个哈希映射,它的键表示某一个方向,值是一个二元组,分别表示当前距离最近的皇后以及对应的距离。当我们枚举到一个新的皇后时,如果它在国王的八个方向上,就与哈希映射中对应的值比较一下大小关系即可。

当枚举完所有皇后,我们就可以从哈希映射值的部分中得到答案。

代码:

class Solution {
public:
    vector<vector<int>> queensAttacktheKing(vector<vector<int>>& queens, vector<int>& king) {
        auto sgn = [](int x) -> int{
            return x > 0 ? 1 : (x == 0 ? 0 : -1);
        };

        unordered_map<int, pair<vector<int>, int>> candidates;
        int kx = king[0], ky = king[1];
        for (const auto& queen: queens) {
            int qx = queen[0], qy = queen[1];
            int x = qx - kx, y = qy - ky;
            if (x == 0 || y == 0 || abs(x) == abs(y)) {
                int dx = sgn(x), dy = sgn(y);
                int key = dx * 10 + dy;
                if (!candidates.count(key) || candidates[key].second > abs(x) + abs(y)) {
                    candidates[key] = {queen, abs(x) + abs(y)};
                }
            }
        }

        vector<vector<int>> ans;
        for (const auto& [_, value]: candidates) {
            ans.push_back(value.first);
        }
        return ans;
    }
};

时间 0ms 击败 100.00%使用 C++ 的用户
内存 10.85MB 击败 31.25%使用 C++ 的用户
复杂度分析

  • 时间复杂度:O(n),其中 n 是数组 queens 的长度。
  • 空间复杂度:O(1)。
    author:力扣官方题解

你可能感兴趣的:(LeetCode,数据结构,leetcode,c++)