Medium
题目描述
给定一个正整数 n,表示一个 n x n 的城市。还给定一个二维网格 buildings,其中 buildings[i] = [x, y] 表示位于坐标 [x, y] 的一个唯一建筑物。
如果一个建筑物在所有四个方向(左、右、上、下)都至少有一个建筑物,则该建筑物被覆盖。
返回被覆盖的建筑物数量。
示例 1:
输入:n = 3, buildings = [[1,2],[2,2],[3,2],[2,1],[2,3]]
输出:1
解释:
只有建筑物 [2,2] 被覆盖,因为它在四个方向都有至少一个建筑物:
- 上方 ([1,2])
- 下方 ([3,2])
- 左方 ([2,1])
- 右方 ([2,3])
因此,被覆盖的建筑物数量为 1。
示例 2:
输入:n = 3, buildings = [[1,1],[1,2],[2,1],[2,2]]
输出:0
解释:
没有建筑物在四个方向都有建筑物。
示例 3:
输入:n = 5, buildings = [[1,3],[3,2],[3,3],[3,5],[5,3]]
输出:1
解释:
只有建筑物 [3,3] 被覆盖,因为它在四个方向都有至少一个建筑物:
- 上方 ([1,3])
- 下方 ([5,3])
- 左方 ([3,2])
- 右方 ([3,5])
因此,被覆盖的建筑物数量为 1。
约束条件:
2 <= n <= 10^51 <= buildings.length <= 10^5buildings[i] = [x, y]1 <= x, y <= n- 所有建筑物的坐标都是唯一的。
解题思路
这道题需要判断每个建筑物在四个方向(上下左右)是否都有其他建筑物。关键思路是:
基本思路: 一个建筑物被覆盖的条件是在同一行和同一列都不是边界建筑。具体来说:
- 在同一行中,不是最左边或最右边的建筑物
- 在同一列中,不是最上面或最下面的建筑物
优化解法:
- 按行分组:将所有建筑物按行(x坐标)分组,对每行的y坐标排序
- 按列分组:将所有建筑物按列(y坐标)分组,对每列的x坐标排序
- 标记覆盖状态:
- 对于每行,标记不在首尾位置的建筑物为"行覆盖"
- 对于每列,标记不在首尾位置的建筑物为"列覆盖"
- 统计结果:只有同时满足"行覆盖"和"列覆盖"的建筑物才是被完全覆盖的
这种方法的优势是只需要对每个方向排序一次,然后用集合操作找到同时满足两个条件的建筑物,时间复杂度较低。
代码实现
class Solution {
public:
int countCoveredBuildings(int n, vector<vector<int>>& buildings) {
unordered_map<int, vector<int>> rows, cols;
// Group buildings by row and column
for (auto& building : buildings) {
rows[building[0]].push_back(building[1]);
cols[building[1]].push_back(building[0]);
}
// Sort each group
for (auto& [x, ys] : rows) {
sort(ys.begin(), ys.end());
}
for (auto& [y, xs] : cols) {
sort(xs.begin(), xs.end());
}
set<pair<int, int>> rowCovered, colCovered;
// Find buildings covered in rows (not first or last in their row)
for (auto& [x, ys] : rows) {
if (ys.size() > 2) {
for (int i = 1; i < ys.size() - 1; i++) {
rowCovered.insert({x, ys[i]});
}
}
}
// Find buildings covered in columns (not first or last in their column)
for (auto& [y, xs] : cols) {
if (xs.size() > 2) {
for (int i = 1; i < xs.size() - 1; i++) {
colCovered.insert({xs[i], y});
}
}
}
// Count buildings covered in both directions
int count = 0;
for (auto& building : rowCovered) {
if (colCovered.count(building)) {
count++;
}
}
return count;
}
};
class Solution:
def countCoveredBuildings(self, n: int, buildings: List[List[int]]) -> int:
from collections import defaultdict
rows = defaultdict(list)
cols = defaultdict(list)
# Group buildings by row and column
for x, y in buildings:
rows[x].append(y)
cols[y].append(x)
# Sort each group
for ys in rows.values():
ys.sort()
for xs in cols.values():
xs.sort()
row_covered = set()
col_covered = set()
# Find buildings covered in rows (not first or last in their row)
for x, ys in rows.items():
if len(ys) > 2:
for i in range(1, len(ys) - 1):
row_covered.add((x, ys[i]))
# Find buildings covered in columns (not first or last in their column)
for y, xs in cols.items():
if len(xs) > 2:
for i in range(1, len(xs) - 1):
col_covered.add((xs[i], y))
# Count buildings covered in both directions
return len(row_covered & col_covered)
public class Solution {
public int CountCoveredBuildings(int n, int[][] buildings) {
var rows = new Dictionary<int, List<int>>();
var cols = new Dictionary<int, List<int>>();
// Group buildings by row and column
foreach (var building in buildings) {
int x = building[0], y = building[1];
if (!rows.ContainsKey(x)) rows[x] = new List<int>();
if (!cols.ContainsKey(y)) cols[y] = new List<int>();
rows[x].Add(y);
cols[y].Add(x);
}
// Sort each group
foreach (var ys in rows.Values) {
ys.Sort();
}
foreach (var xs in cols.Values) {
xs.Sort();
}
var rowCovered = new HashSet<(int, int)>();
var colCovered = new HashSet<(int, int)>();
// Find buildings covered in rows (not first or last in their row)
foreach (var kvp in rows) {
int x = kvp.Key;
var ys = kvp.Value;
if (ys.Count > 2) {
for (int i = 1; i < ys.Count - 1; i++) {
rowCovered.Add((x, ys[i]));
}
}
}
// Find buildings covered in columns (not first or last in their column)
foreach (var kvp in cols) {
int y = kvp.Key;
var xs = kvp.Value;
if (xs.Count > 2) {
for (int i = 1; i < xs.Count - 1; i++) {
colCovered.Add((xs[i], y));
}
}
}
// Count buildings covered in both directions
int count = 0;
foreach (var building in rowCovered) {
if (colCovered.Contains(building)) {
count++;
}
}
return count;
}
}
var countCoveredBuildings = function(n, buildings) {
const rows = new Map();
const cols = new Map();
// Group buildings by row and column
for (const [x, y] of buildings) {
if (!rows.has(x)) rows.set(x, []);
if (!cols.has(y)) cols.set(y, []);
rows.get(x).push(y);
cols.get(y).push(x);
}
// Sort each group
for (const ys of rows.values()) {
ys.sort((a, b) => a - b);
}
for (const xs of cols.values()) {
xs.sort((a, b) => a - b);
}
const rowCovered = new Set();
const colCovered = new Set();
// Find buildings covered in rows (not first or last in their row)
for (const [x, ys] of rows) {
if (ys.length > 2) {
for (let i = 1; i < ys.length - 1; i++) {
rowCovered.add(`${x},${ys[i]}`);
}
}
}
// Find buildings covered in columns (not first or last in their column)
for (const [y, xs] of cols) {
if (xs.length > 2) {
for (let i = 1; i < xs.length - 1; i++) {
colCovered.add(`${xs[i]},${y}`);
}
}
}
// Count buildings covered in both directions
let count = 0;
for (const building of rowCovered) {
if (colCovered.has(building)) {
count++;
}
}
return count;
};
复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间复杂度 | O(m log m) |
| 空间复杂度 | O(m) |
其中 m 是建筑物的数量。时间复杂度主要由排序操作决定,空间复杂度用于存储分组后的建筑物和覆盖状态集合。