73. 矩阵置零

思路与解法
1、标记阶段:遍历整个矩阵,遇到 0 元素时,记录下它所在的行和列(用两个布尔数组 row 和 col 分别标记)。
2、置零阶段:再次遍历矩阵,对于每个元素,检查其所在行或列是否被标记,如果是则将其置零。
cpp
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size(); //获取矩阵的行数
int n = matrix[0].size();//获取矩阵的列数
//创建两个标记数组:row用于标记需要置零的行,col用于标记需要置零的列
vector<int> row(m);
vector<int> col(n);
//第一次遍历:扫描整个矩阵,记录哪些行和列包含0
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(!matrix[i][j]){
row[i]=true;
col[j]=true;
}
}
}
//第二次遍历:根据标记数组,将对应行和列的元素置零
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(row[i]||col[j]){
matrix[i][j]=0;
}
}
}
}
};
ACM:
cpp
#include <vector>
#include <iostream>
using namespace std;
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
vector<int> row(m);
vector<int> col(n);
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(!matrix[i][j]){
row[i]=true;
col[j]=true;
}
}
}
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(row[i]||col[j]){
matrix[i][j]=0;
}
}
}
}
};
int main(){
int m, n;
cin >> m >> n;
vector<vector<int>> matrix(m,vector<int>(n));
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
cin>>matrix[i][j];
Solution sol;
sol.setZeroes(matrix);
for(auto& row:matrix){
bool first=true;
for(int num:row){
if(!first) cout<<" "; //如果不是第一个,先输出空格
cout<<num;
first=false;
}
cout<<endl;
}
}
54.螺旋矩阵

思路与解法
本题并不涉及到什么算法,就是模拟过程,但却十分考察对代码的掌控能力。一定要明确好边界!!
核心是模拟螺旋遍历矩阵的过程。遍历方式如下:
从左到右遍历上边界
从上到下遍历右边界
从右到左遍历下边界(如果仍然存在)
从下到上遍历左边界(如果仍然存在)
然后不断缩小边界范围,直到遍历完整个矩阵。
代码使用的是左闭右闭区间。即每一圈的遍历范围包括边界上的所有元素,循环条件使用 <= 来包含起始和结束位置。遍历完一条边后立即收缩对应边界,确保下一圈不会重复访问已处理的元素。
算法设计
我们可以定义四个变量来表示边界:
top表示上边界,bottom表示下边界
left表示左边界,right表示右边界
遍历过程中按照顺时针方向进行:
① 遍历top行,从left到right,然后top++
② 遍历right列,从top到bottom,然后right- -
③ 如果仍然有bottom行,则从right到left遍历该行,bottom- -
④ 如果仍然有left列,则从bottom到top遍历该列,left++
不断重复上述过程,直到所有元素都被访问。
为什么需要这两个 if?
在每一圈的遍历中,我们依次处理四条边:
上边:从左到右(一定存在)
右边:从上到下(一定存在,因为至少有一行一列)
下边:从右到左(可能不存在)
左边:从下到上(可能不存在)
理清楚这4条边用什么变量即可。先画个图!!

核心代码:
cpp
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> results;
if(matrix.empty()||matrix[0].empty()) return results;
int m = matrix.size();
int n = matrix[0].size();
int top = 0,bottom = m-1;
int left =0,right = n-1;
while(top<=bottom && left<=right){
for(int i=left;i<=right;i++){
results.push_back(matrix[top][i]);
}
top++;
for(int i=top;i<=bottom;i++){
results.push_back(matrix[i][right]);
}
right--;
if(top<=bottom){
for(int i=right;i>=left;i--){
results.push_back(matrix[bottom][i]);
}
bottom--;
}
if(left<=right){
for(int i=bottom;i>=top;i--){
results.push_back(matrix[i][left]);
}
left++;
}
}
return results;
}
};
【注】
1、为何下两种情况要用if?
当矩阵只有一行(top == bottom)时,遍历完上边(即整行)后,top 增加,导致 top > bottom。此时下边与上边其实是同一行,如果继续遍历下边,就会把这一行的元素再反向遍历一遍,造成重复。
同样,当矩阵只有一列(left == right)时,遍历完上边和右边后,right 减少,导致 left > right。此时左边与右边其实是同一列,如果继续遍历左边,也会重复访问该列的元素。
因此,必须在遍历下边之前检查是否还有多行(top <= bottom),在遍历左边之前检查是否还有多列(left <= right)。这两个条件确保了在边界收缩后,我们只遍历实际存在的边。
注意 bottom--;和left++; 要放在if里面!!!
2、统一左闭右闭,即for循环里面都取=(<=、>=)!!
48. 旋转图像

思路与解法
先转置,然后水平翻转!!!


核心代码:
cpp
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n=matrix.size();
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
swap(matrix[i][j],matrix[j][i]);
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n/2;j++){
swap(matrix[i][j],matrix[i][n-1-j]);
}
}
}
};
【注】
1、 for (int j = i + 1; j < n; j++) {
内层循环从 j = i + 1开始,只处理上三角(i < j),避免重复交换,保证每个非对角线元素只被交换一次。
2、for (int j = 0; j < n / 2; j++) {
只需要遍历每一行的左半部分,因为右半部分会在交换时自动被处理。
ACM:
cpp
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n=matrix.size();
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
swap(matrix[i][j],matrix[j][i]);
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n/2;j++){
swap(matrix[i][j],matrix[i][n-1-j]);
}
}
}
};
int main(){
int n;
cin>>n;
vector<vector<int>> matrix(n,vector<int>(n));
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>matrix[i][j];
}
}
Solution sol;
sol.rotate(matrix);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(j>0) cout<<" ";
cout<<matrix[i][j];
}
cout<<endl;
}
return 0;
}
【注】
1、 if(j>0) cout<<" ";
注意是j>0!!
240.搜索二维矩阵 II

思路与解法
本题要求高效搜索,最优解法为"Z 字型查找",时间复杂度 O(m + n)。
核心思路:
由于矩阵行递增、列递增,我们可以从右上角出发,往左下移动 :
如果当前值 matrix[i][j] == target,返回 true。
如果当前值 > target,左移(j- -) 。
如果当前值 < target,下移(i++)。
若超出矩阵边界,则返回 false。
核心代码:
cpp
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size(),n= matrix[0].size();
int i=0,j=n-1;
while(i<m&&j>=0){
if(matrix[i][j]==target) return true;
else if(matrix[i][j]>target) j--;
else i++;
}
return false;
}
};
【注】
1、while (i < m && j >= 0)
确保行索引不超出下边界(0...m-1),列索引不超出左边界(0...n-1)。
ACM:
cpp
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size(),n= matrix[0].size();
int i=0,j=n-1;
while(i<m&&j>=0){
if(matrix[i][j]==target) return true;
else if(matrix[i][j]>target) j--;
else i++;
}
return false;
}
};
int main(){
int m,n,target;
cin>>m>>n;
vector<vector<int>> matrix(n,vector<int>(n));
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
cin>>matrix[i][j];
}
}
cin>>target;
Solution sol;
cout<<(sol.searchMatrix(matrix,target)?"true":"false")<<endl;
return 0;
}