开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情
一 描述
1337. 矩阵中战斗力最弱的 K 行 - 力扣(LeetCode) (leetcode-cn.com)
给你一个大小为 m * n
的矩阵 mat
,矩阵由若干军人和平民组成,分别用 1
和 0
表示。
请你返回矩阵中战斗力最弱的 k
行的索引,按从最弱到最强排序。
如果第 i
行的军人数量少于第 j
行,或者两行军人数量相同但 i
小于 j
,那么我们认为第 i
行的战斗力比第 j
行弱。
军人 总是 排在一行中的靠前位置,也就是说 1 总是出现在 0 之前。
示例 1:
输入:mat =
[[1,1,0,0,0],
[1,1,1,1,0],
[1,0,0,0,0],
[1,1,0,0,0],
[1,1,1,1,1]],
k = 3
输出:[2,0,3]
解释:
每行中的军人数目:
行 0 -> 2
行 1 -> 4
行 2 -> 1
行 3 -> 2
行 4 -> 5
从最弱到最强对这些行排序后得到 [2,0,3,1,4]
示例 2:
输入:mat =
[[1,0,0,0],
[1,1,1,1],
[1,0,0,0],
[1,0,0,0]],
k = 2
输出:[0,2]
解释:
每行中的军人数目:
行 0 -> 1
行 1 -> 4
行 2 -> 1
行 3 -> 1
从最弱到最强对这些行排序后得到 [0,2,3,1]
提示:
- m == mat.length
- n == mat[i].length
- 2 <= n, m <= 100
- 1 <= k <= m
- matrix[i][j] 不是 0 就是 1
二 分析
题目要求我们求出战斗力最弱的 k 行,因此我们需要先统计每行的战斗力(即 1 的个数),然后再从中取出 1 的个数最少的 k 行。
对于如何统计每行的 1 的个数,注意到题目条件:军人总是排在一行中的靠前位置,也就是说 1 总是出现在 0 之前,因此我们可以直接搜索 1 和 0 的分界的地方,找到这个分界点不就知道了 1 的个数了。
对于怎样找出所有行中 1 的个数最少的 k 行,可以使用堆这种数据结构,每次从堆中取出 1 的个数最少的那行。
三 答案
class Solution {
public:
struct cmp {
bool operator() (const pair<int, int>& a, const pair<int, int>& b) {
//如果两行的战斗力相同,我们就比较下标
if (a.second == b.second) {
return a.first > b.first;
}
return a.second > b.second;
}
};
vector<int> kWeakestRows(vector<vector<int>>& mat, int k) {
priority_queue<pair<int, int>, //第一元素代表行的下标,第二元素代表行的战斗力
vector<pair<int, int>>,
cmp> pque;
int n = mat.size(), m = mat[0].size();
//对矩阵中的每行进行二分法
for (int i = 0; i < n; ++i) {
int left = 0, right = m;
while (left < right) {
int mid = (right - left) / 2 + left;
if (mat[i][mid] == 0) {
right = mid;
} else {
left = mid + 1;
}
}
//找到了分界点,就放进堆中
pque.push({i, left});
}
vector<int> res;
//从优先队列中取出战斗力最弱的 k 行
while (k--) {
res.push_back(pque.top().first);
pque.pop();
}
return res;
}
};