LeetCode——1337. 矩阵中战斗力最弱的 K 行(中等)——二分查找 + 堆/快速选择

我的解法:有点疲惫,就恩写,来回查表

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%+,去看看官方答案吧

官方答案:比我想象中复杂很多诶咱就是说

力扣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/

前言:
        由于本题中的矩阵行数 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) 怎么来的?」了解该过程时间复杂度的证明方法。

堆排序中建堆过程时间复杂度O(n)怎么来的? - 知乎堆排序中建堆过程时间复杂度O(n)怎么来的?《算法导论》中说:Max-Heapify作用在高度为h的节点上的时间为…https://www.zhihu.com/question/20729324

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;
    }
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值