被这道题折磨了一上午,解法好想,但是扣边界真的是太!恶!心!了!!!!!!!!
于是乎我决定把所有矩阵相关扣边界的题都挖过来,就是干!
先来看看折磨我一上午的小妖精
题目一——zigzag方式打印矩阵
用zigzag的方式打印矩阵,比如如下的矩阵
0 1 2 3
4 5 6 7
8 9 10 11
打印顺序为:0 1 4 8 5 2 3 6 9 10 7 11
我的问题主要出在了变量名上,二维数组a[i][j]里面,i是行,对应了直角坐标里面的y,j是列,对应了直角坐标里面的x。以后二维矩阵的题,我变量名都取行列吧,再也不取xy了,绕晕了,啊啊啊啊啊。
思路:
用两个指针A和B,用(xa,ya)表示A的位置,(xb,yb)表示B的位置。依次移动这两个指针,使得他们每次都处在一条斜线上。置于来回打印,可以用一个bool变量来控制打印方向即可。
上代码。
代码里还是有很多边界需要考虑的,比如print函数里面的while循环,两个变量的比较顺序放反了,就会导致你少打印点东西,具体例子看代码的注释
/*
用zigzag的方式打印矩阵,比如如下的矩阵
0 1 2 3
4 5 6 7
8 9 10 11
打印顺序为:0 1 4 8 5 2 3 6 9 10 7 11
*/
#include<iostream>
#include<vector>
using namespace std;
void helper(vector<vector<int>>&num, int av, int ah, int bv, int bh, bool flag){
//打印顺序,先左上(flag=true时),后右下(flag=false时)
if(!flag){
//向左下方打印(坐标和二维矩阵的是不一样的,阿西吧)
//从(0,2)走到(2,0)
while(ah!=bh-1)
cout<<num[av++][ah--]<<endl;
}else{
//向左上方打印
//从(1,0)到(0,1)
while(bv!=av-1)
cout<<num[bv--][bh++]<<endl;
}
}
void Print(vector<vector<int>>&num){
if(num.size()==0) return;
int av=0, ah=0, bv=0, bh=0;
bool flag=true;
int hor=num[0].size()-1, ver=num.size()-1;
while(av!=(ver+1)){
helper(num, av, ah, bv, bh, flag);
//av为什么写在ah的上面?思考情况:
//ah=hor-1时,如果ah写在上面,则ah=ah+1,此时ah=hor,
//本来应该指向矩阵右上角的,结果av一看ah=hor了,就猥琐的执行了 av=av+1
av=ah==hor?av+1:av;
ah=ah==hor?ah:ah+1;
bh=bv==ver?bh+1:bh;
bv=bv==ver?bv:bv+1;
flag=!flag;
}
}
int main(){
vector<vector<int>>num={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
Print(num);
return 0;
}
题目二——用螺旋的方式打印矩阵,比如如下的矩阵
用螺旋的方式打印矩阵,比如如下的矩阵
0 1 2 3
4 5 6 7
8 9 10 11
打印顺序为:0 1 2 3 7 11 10 9 8 4 5 6
不要跟着打印的线路走,宏观去看,每次都是打印最外面的一圈(如下图),所以可以设置两个点,一个指向矩形的左上角(ar和ac分别表示a行和a列),一个指向矩形的右下角。每次打印完一圈。就把左上角的向右下角移动一位,同理右上角的指针向左上角移动一位。
打印会遇到两个特殊情况
1、ar=br 即两个指针指向了同一行元素(例子看下侧实现代码的helper函数)
2、ac=bc 即两个指针指向了同一列元素(例子看下侧实现代码的helper函数)
对于这两种情况,在打印的时候要区分出来
#include<iostream>
#include<vector>
using namespace std;
void helper(vector<vector<int>>& matrix,
vector<int>&res, int ar, int ac, int br, int bc){
//说明此时要打印一条水平线
/*
1234
5678
1234
当打印中间的67的时候,会出现这种情况
*/
if(ar==br){
while(ac<bc+1){
res.push_back(matrix[ar][ac++]);
}
//此时打印一列的情况
/*
123
456
789
123
当打印中间的那一竖58的时候,会出现该种情况
*/
}else if(ac==bc){
while(ar<br+1){
res.push_back(matrix[ar++][ac]);
}
}else{
int hor=ac, ver=ar;
//打印上面的横向边框
while(hor<bc){
res.push_back(matrix[ar][hor++]);
}
//打印右侧的纵向边框
while(ver<br){
res.push_back(matrix[ver++][hor]);
}
//打印下方的横向边框
while(hor>ac){
res.push_back(matrix[ver][hor--]);
}
//打印左侧的竖向边框
while(ar<ver){
res.push_back(matrix[ver--][hor]);
}
}
}
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int>res;
if((matrix.size()==0)||(matrix[0].size()==0)) return res;
int ar=0, ac=0, br=matrix.size()-1, bc=matrix[0].size()-1;
while(1){
//打印边框的函数
helper(matrix, res,ar,ac,br,bc);
//打印之后移动指针,以便于打印内层的边框
ar++;
ac++;
br--;
bc--;
if((ar>br)||(ac>bc)) break;
}
for(auto i:res)
cout<<i<<" ";
return res;
}
int main(){
vector<vector<int>>matrix={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
spiralOrder(matrix);
cout<<endl;
return 0;
}
这题是leetcode 54题
上AC代码
class Solution {
public:
void helper(vector<vector<int>>& matrix,
vector<int>&res, int ar, int ac, int br, int bc){
//说明此时要打印一条水平线(ar=br的情况)
if(ar==br){
while(ac<bc+1){
res.push_back(matrix[ar][ac++]);
}
}else if(ac==bc){
while(ar<br+1){
res.push_back(matrix[ar++][ac]);
}
}else{
int hor=ac, ver=ar;
while(hor<bc){
res.push_back(matrix[ver][hor++]);
}
while(ver<br){
res.push_back(matrix[ver++][hor]);
}
while(hor>ac){
res.push_back(matrix[ver][hor--]);
}
while(ar<ver){
res.push_back(matrix[ver--][hor]);
}
}
}
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int>res;
if((matrix.size()==0)||(matrix[0].size()==0)) return res;
//设置两个角的坐标
int ar=0, ac=0, br=matrix.size()-1, bc=matrix[0].size()-1;
while(1){
helper(matrix, res,ar,ac,br,bc);
ar++;
ac++;
br--;
bc--;
if((ar>br)||(ac>bc)) break;
}
return res;
}
};
题目三——给定一个正方形矩阵,只用有限几个变量,实现矩阵中每个位置的数顺时针转动
给定一个正方形矩阵,只用有限几个变量,实现矩阵中每个位置的数顺时针转动
90度,比如如下的矩阵
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
矩阵应该被调整为:
12 8 4 0
13 9 5 1
14 10 6 2
15 11 7 3
思路
还是要宏观,假设一行有k个元素,这个矩阵里面的所有元素可以分成 k-1 组,每组四个元素。前提,这个矩阵一定是正方形,不是正方形你没办法转。对应的第 i 组的地个点的坐标为m[a][b+1] , m[a+i][d] , m[c][d-i] , m[c-i][b]. 每次旋转的时候,就是把这四个位置的值交换即可。
#include<iostream>
#include<vector>
using namespace std;
void helper(vector<vector<int>>&matrix, int a, int b, int c, int d){
int temp=0;
int time=d-c;
for(int i=0; i<time; i++){
temp=matrix[c-i][b];
matrix[c-i][b]=matrix[c][d-i];
matrix[c][d-i]=matrix[a+i][d];
matrix[a+i][d]=matrix[a][b+i];
matrix[a][b+i]=temp;
}
}
void rotate(vector<vector<int>>&matrix){
int a=0, b=0, c=matrix.size()-1, d=matrix[0].size()-1;
while(a<=c&&b<=d)
helper(matrix, a++, b++, c--, d--);
}
int main(){
vector<vector<int>>matrix={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
rotate(matrix);
return 0;
}
这题是leetcode 48 旋转图像
class Solution {
public:
void helper(vector<vector<int>>& matrix, int a, int b, int c, int d){
int temp=0;
//将四个点的元素相互交换
for(int i=0; i<d-b; i++){
temp=matrix[c-i][b];
matrix[c-i][b]=matrix[c][d-i];
matrix[c][d-i]=matrix[a+i][d];
matrix[a+i][d]=matrix[a][b+i];
matrix[a][b+i]=temp;
}
}
void rotate(vector<vector<int>>& matrix) {
int a=0, b=0, c=matrix.size()-1, d=matrix[0].size()-1;
//选定四个点,开始转框
while(a<=c&&b<=d)
helper(matrix, a++,b++,c--,d--);
}
};