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