我的解法:有点疲惫,就恩写,来回查表
class Solution {
public:
vector<int> kWeakestRows(vector<vector<int>>& mat, int k) {
vector<int> rank;//建立战斗力表
for(auto a:mat){
rank.push_back(count(a.cbegin(),a.cend(),1));
}
multimap<int,int> rank1;//建立战力——序号对照表
int i=-1;
for(auto a:mat){
rank1.insert({count(a.cbegin(),a.cend(),1),(++i)});
}
int m=mat.size();//对战斗力表进行排序
for(int i=0;i<m-1;i++){
for(int j=i+1;j<m;j++){
if(rank[i]>rank[j]){
swap(rank[i],rank[j]);
}
}
}
vector<int> finalrank;//根据战斗力查表,建立最终按战斗力排序表
for(int i=0;i<m;i++){
finalrank.push_back(rank1.find(rank[i])->second);
rank1.erase(rank1.find(rank[i]));//删除以入表元素,避免multimap中重复
}
return vector<int>(finalrank.cbegin(),finalrank.cbegin()+k);
}
};
时间:50%+;空间:5%,评价为啥也不是
别人解法:
这老哥是我的简化版吗,但仍有不少改进空间:
class Solution {
public:
vector<int> kWeakestRows(vector<vector<int>>& mat, int k) {
vector<int> v;
if(mat.size()==0)
{
return v;
}
multimap<int,int> m;
for(int i=0;i<mat.size();i++)
{
int sum=0;
for(int j=0;j<mat[0].size();j++)
{
sum+=mat[i][j];
}
m.insert(make_pair(sum,i));
}
multimap<int,int>::iterator it=m.begin();
for(int i=0;i<k;i++)
{
v.push_back(it->second);
it++;
}
return v;
}
};
时间:50%+,空间:15%+,还是不太够捏
作者:cun-xia-chong-shu
链接:https://leetcode-cn.com/problems/the-k-weakest-rows-in-a-matrix/solution/by-cun-xia-chong-shu-5m23/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
看着老哥写法想起来multimap里面自动升序,那我的原写法可略作修改:
class Solution {
public:
vector<int> kWeakestRows(vector<vector<int>>& mat, int k) {
multimap<int,int> rank1;
int i=-1;
for(auto a:mat){
rank1.insert({count(a.cbegin(),a.cend(),1),(++i)});
}
vector<int> finalrank;
auto it=rank1.cbegin();
for(int i=0;i<k;i++){
finalrank.push_back(it->second);
it++;
}
return finalrank;
}
};
时间:90%+,空间:5%+,去看看官方答案吧
官方答案:比我想象中复杂很多诶咱就是说
前言:
由于本题中的矩阵行数 m 和列数 n 均不超过 100,数据规模较小,因此我们可以设计出一些时间复杂度较高的方法,例如直接对整个矩阵进行一次遍历,计算出每一行的战斗力,再进行排序并返回最弱的 k 行的索引。
下面我们根据矩阵的性质,给出一种时间复杂度较为优秀的方法。
方法一:二分查找 + 堆
思路与算法:
题目描述中有一条重要的保证:
军人总是排在一行中的靠前位置,也就是说 1 总是出现在 0 之前。
因此,我们可以通过二分查找的方法,找出一行中最后的那个 1 的位置:
如果其位置为 pos,那么这一行 1 的个数就为 pos+1。特别地,如果这一行没有 1,那么令 pos=−1。
当我们得到每一行的战斗力后,我们可以将它们全部放入一个小根堆中,并不断地取出堆顶的元素 k 次,这样我们就得到了最弱的 k 行的索引。
需要注意的是,如果我们依次将每一行的战斗力以及索引(因为如果战斗力相同,索引较小的行更弱,所以我们需要在小根堆中存放战斗力和索引的二元组)放入小根堆中,那么这样做的时间复杂度是 O(mlogm) 的。一种更好的方法是使用这 m 个战斗力值直接初始化一个小根堆,时间复杂度为 O(m)。读者可以参考《算法导论》的 6.3 节或者「堆排序中建堆过程时间复杂度 O(n) 怎么来的?」了解该过程时间复杂度的证明方法。
class Solution {
public:
vector<int> kWeakestRows(vector<vector<int>>& mat, int k) {
int m = mat.size(), n = mat[0].size();
vector<pair<int, int>> power;
for (int i = 0; i < m; ++i) {
int l = 0, r = n - 1, pos = -1;
while (l <= r) {
int mid = (l + r) / 2;
if (mat[i][mid] == 0) {
r = mid - 1;
}
else {
pos = mid;
l = mid + 1;
}
}
power.emplace_back(pos + 1, i);//或者power.emplace_back(l, i),前者可读性更好
}
/*优先队列:普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。
在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。
优先队列具有最高级先出 (first in, largest out)的行为特征。
通常采用堆数据结构来实现。*/
priority_queue q(greater<pair<int, int>>(), move(power));
vector<int> ans;
for (int i = 0; i < k; ++i) {
ans.push_back(q.top().second);
q.pop();
}
return ans;
}
};
复杂度分析:
时间复杂度:O(mlogn+klogm):
- 我们需要 O(mlogn) 的时间对每一行进行二分查找。
- 我们需要 O(m) 的时间建立小根堆。
- 我们需要 O(klogm) 的时间从堆中取出 k 个最小的元素。
空间复杂度:O(m),即为堆需要使用的空间。
时间:50%+,空间:50%+,不错,不偏科
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/the-k-weakest-rows-in-a-matrix/solution/fang-zhen-zhong-zhan-dou-li-zui-ruo-de-k-xing-by-l/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法二:二分查找 + 快速选择
思路与算法:
我们也可以通过快速选择算法,在平均 O(m) 的时间内不计顺序地内找出 k 个最小的元素,再使用排序算法在 O(klogk) 的时间对这 k 个最小的元素进行升序排序,就可以得到最终的答案。读者可以参考「剑指 Offer 40. 最小的k个数」官方题解的方法三或者「215. 数组中的第K个最大元素」的官方题解中的方法一了解快速选择算法,下面的代码将上述题解中的快速选择算法封装成一个 Helper 类进行使用。
力扣https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/zui-xiao-de-kge-shu-by-leetcode-solution/力扣https://leetcode-cn.com/problems/kth-largest-element-in-an-array/solution/shu-zu-zhong-de-di-kge-zui-da-yuan-su-by-leetcode-/
template<typename T>
class Helper {
static int partition(vector<T>& nums, int l, int r) {
T pivot = nums[r];
int i = l - 1;
for (int j = l; j <= r - 1; ++j) {
if (nums[j] <= pivot) {
i = i + 1;
swap(nums[i], nums[j]);
}
}
swap(nums[i + 1], nums[r]);
return i + 1;
}
// 基于随机的划分
static int randomized_partition(vector<T>& nums, int l, int r) {
int i = rand() % (r - l + 1) + l;
swap(nums[r], nums[i]);
return partition(nums, l, r);
}
static void randomized_selected(vector<T>& arr, int l, int r, int k) {
if (l >= r) {
return;
}
int pos = randomized_partition(arr, l, r);
int num = pos - l + 1;
if (k == num) {
return;
} else if (k < num) {
randomized_selected(arr, l, pos - 1, k);
} else {
randomized_selected(arr, pos + 1, r, k - num);
}
}
public:
static vector<T> getLeastNumbers(vector<T>& arr, int k) {
srand((unsigned)time(NULL));
randomized_selected(arr, 0, (int)arr.size() - 1, k);
vector<T> vec;
for (int i = 0; i < k; ++i) {
vec.push_back(arr[i]);
}
return vec;
}
};
class Solution {
public:
vector<int> kWeakestRows(vector<vector<int>>& mat, int k) {
int m = mat.size(), n = mat[0].size();//二分查找
vector<pair<int, int>> power;
for (int i = 0; i < m; ++i) {
int l = 0, r = n - 1, pos = -1;
while (l <= r) {
int mid = (l + r) / 2;
if (mat[i][mid] == 0) {
r = mid - 1;
}
else {
pos = mid;
l = mid + 1;
}
}
power.emplace_back(pos + 1, i);
}
//快速选择
vector<pair<int, int>> minimum = Helper<pair<int, int>>::getLeastNumbers(power, k);
sort(minimum.begin(), minimum.begin() + k);
vector<int> ans;
for (int i = 0; i < k; ++i) {
ans.push_back(minimum[i].second);
}
return ans;
}
};
复杂度分析
时间复杂度:O(mlogn+klogk):
- 我们需要 O(mlogn) 的时间对每一行进行二分查找。
- 我们需要 O(m) 的时间完成快速选择算法。
- 我们需要 O(klogk) 的时间对这 k 个最小的元素进行升序排序。
空间复杂度:O(m),即为快速选择算法中的数组需要使用的空间。
这个有点神经刀,时间90%+~20%,空间70%+~20%差异就很大,因为随机算法吗?明天就写快速选择吧
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/the-k-weakest-rows-in-a-matrix/solution/fang-zhen-zhong-zhan-dou-li-zui-ruo-de-k-xing-by-l/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一些其他算法:
百倍算法:
由于矩阵行列<=100,所以可以用此方(战力*100+序号)比较不影响相对战力大小
class Solution {
public:
vector<int> kWeakestRows(vector<vector<int>>& a, int k) {
vector<int>ans;
vector<int>p;
//二分查找
for(int i=0;i<a.size();i++){
int l=0,r=a[i].size()-1,ans=0;
while(l<=r){
int mid=l+(r-l)/2;
if(a[i][mid]==0){
r=mid-1;
}else {
l=mid+1;
ans=mid+1;
}
}
p.push_back(ans*100+i);/*由于矩阵行列<=100,
所以可以用此方法比较不影响相对战力大小*/
}
sort(p.begin(),p.end());
for(int i=0;i<k;i++)ans.push_back(p[i]%100);
return ans;
}
};