LeetCode 矩阵

☺☺☺

(1)378. 有序矩阵中第K小的元素 【中等】

给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。
请注意,它是排序后的第k小元素,而不是第k个元素。

示例:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
返回 13。

法1:最容易想到的,暴力sort

class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        vector<int> temp;
        for(int i=0; i<matrix.size();i++){
            for(int j=0; j<matrix[0].size(); j++){
                temp.push_back(matrix[i][j]);
            }
        }
        sort(temp.begin(), temp.end());
        return temp[k-1];
    }
};

法2:用优先队列 内部有排序操作
优先队列 priority_queue: 默认队头元素最大 (如果需要一个小顶堆,将仿函数 less 修改为 greater)
不出所料,priority_queue 容器适配器定义了一个元素有序排列的队列。默认队列头部的元素优先级最高。因为它是一个队列,所以只能访问第一个元素,这也意味着优先级最高的元素总是第一个被处理。但是如何定义“优先级”完全取决于我们自己。如果一个优先级队列记录的是医院里等待接受急救的病人,那么病人病情的严重性就是优先级。如果队列元素是银行的借贷业务,那么借记可能会优先于信贷。

priority_queue 操作:

对 priority_queue 进行操作有一些限制:
push(const T& obj):将obj的副本放到容器的适当位置,这通常会包含一个排序操作。
push(T&& obj):将obj放到容器的适当位置,这通常会包含一个排序操作。
emplace(T constructor a rgs...):通过调用传入参数的构造函数,在序列的适当位置构造一个T对象。为了维持优先顺序,通常需要一个排序操作。
top():返回优先级队列中第一个元素的引用。
pop():移除第一个元素。
size():返回队列中元素的个数。
empty():如果队列为空的话,返回true。
swap(priority_queue<T>& other):和参数的元素进行交换,所包含对象的类型必须相同。

在这里插入图片描述

class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        priority_queue<int> pq;  //定义一个优先队列
        for (int i = 0; i < matrix.size(); ++i) {
            for (int j = 0; j < matrix[0].size(); ++j) {
                pq.push(matrix[i][j]);   //把值一个个放入优先队列,会经过一个排序过程
                if (pq.size() > k) pq.pop();  //移除第一个元素
            }
        }
        return pq.top();  //返回优先级队列中第一个元素的引用
    }                     //返回级别最高的元素
};

法3:二分查找法,整体二分查找,在每行二分查找,统计每行小于等于目标数的个数
这。。。谁能想到啊!

/*
O(logm * nlogn) m为元素个数,行数和列数均为n, 平均logm次计算mid(while语句),每行进行二分查找,时间复杂度为O(nlogn)
 
例:
[1 2
12 100]
k = 3
那么刚开始left = 1, right = 100, mid = 50, 遍历完 cnt = 3,此时right更新为50
此时left = 1, right = 50, mid = 25, 遍历完之后 cnt = 3, 此时right更新为25
此时left = 1, right = 25, mid = 13, 遍历完之后 cnt = 3, 此时right更新为13
此时left = 1, right = 13, mid = 7, 遍历完之后 cnt = 2, 此时left更新为8
此时left = 8, right = 13, mid = 10, 遍历完之后 cnt = 2, 此时left更新为11
此时left = 11, right = 12, mid = 11, 遍历完之后 cnt = 2, 此时left更新为12
循环结束,left和right均为12,任意返回一个即可。
*/

class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        int left = matrix[0][0], right = matrix.back().back();
        while (left < right) {//扫描整个矩阵,这里left,right为值,一般的二分查找为索引
            int mid = left + (right - left) / 2, cnt = 0;
            for (int i = 0; i < matrix.size(); ++i) {//扫描每行
                cnt += upper_bound(matrix[i].begin(), matrix[i].end(), mid) - matrix[i].begin();
            }
            if (cnt < k) left = mid + 1;//比k小时,left往中间移,增大新mid,增大count
            else right = mid;//比k大或者等于时,right往中间移
        }//退出循环时left = right,移动至第k小的元素处
        return left;
    }
};

☺☺☺

(2)73. 矩阵置零 【中等】

给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。

示例 1:
输入:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
输出:
[
[1,0,1],
[0,0,0],
[1,0,1]
]
示例 2:
输入:
[
[0,1,2,0],
[3,4,5,2],
[1,3,1,5]
]
输出:
[
[0,0,0,0],
[0,4,5,0],
[0,3,1,0]
]
方法:第一次扫描,用首行首列存储状态,第二次扫描,根据这些状态把相应位置置0。(首行首列单独处理)

class Solution {
public:
    void setZeroes(vector<vector<int>>& a) {
        int rows = a.size();
        int cols = a[0].size();
        bool fr = false; //用于表示首行是否存在0元素的标识变量
        bool fc = false;
        for(int i = 0; i<rows; i++){
            for(int j = 0; j<cols; j++){
                if(a[i][j] == 0){
                    a[i][0] = a[0][j] = 0; //当某个元素为0时,将行首和列首元素置0
                    if(i == 0) fr = true; //第一行有0元素
                    if(j == 0) fc = true; //第一列有0元素
                }
            }
        }
        for(int i = 1; i<rows; i++){//从a11开始扫描,以免破坏首行首列存储信息
            for(int j = 1; j<cols; j++){
                if(a[i][0] == 0 || a[0][j] == 0) a[i][j] = 0;
            }
        } 
        if(fr){
            for(int j = 0; j<cols;j++) a[0][j] = 0; //将第一行置0
        }
        if(fc){
            for(int i = 0; i<rows; i++) a[i][0] = 0; //将第一列置0
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值