本文主要介绍关于多维数组和矩阵的题型,从基础开始逐步深入。
一、顺时针打印矩阵
如下图:
解题方法:把矩阵分解为多个矩形框,从最外层开始打印,每次循环打印一个矩形框,一个循环里用四个while循环打印,每个while循环打印一个矩形的一条边。(用矩阵框的两个顶点进行循环的控制,当左上角的行号小于右下角的行号就退出外层循环)
代码如下:
写法一:
public class 顺时针打印矩阵 {
public static void main(String [] args){
int[][] a = {{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,16}};
print(a);
}
private static void print(int[][] a) {
int leftUpRow = 0;
int leftUpCol = 0;
int rightLowRow = a.length-1;
int rightLowCol = a[0].length-1;
while(leftUpRow <= rightLowRow){
int row = leftUpRow;
int col = leftUpCol;
while(col<=rightLowCol){//打印上面的一行
System.out.print(a[row][col++] + " ");
}
row++;
col--;//恢复
while(row<=rightLowRow){//打印右边的一列
System.out.print(a[row++][col] + " ");
}
col--;
row--;
while(col>=leftUpCol){//打印下面的一行
System.out.print(a[row][col--] + " ");
}
row--;
col++;
while(row>leftUpRow){//打印左边的一列
System.out.print(a[row--][col] + " ");
}
row++;
//将左上角向右下方移, 右下角向左上方移
leftUpRow++; leftUpCol++;
rightLowRow--; rightLowCol--;
}
}
}
写法二:
public static int[] spiralOrder(int[][] matrix) {
if(matrix.length==0 || matrix[0].length==0 || matrix==null) return new int[0];
int[] res = new int[matrix.length*matrix[0].length];
int index = 0; //记录结果集的下标
int leftUpRow = 0; //左上角的坐标
int leftUpCol = 0;
int rightLowRow = matrix.length -1; //右下角的坐标
int rightLowCol = matrix[0].length -1;
//把矩阵分为多层,从最外层开始打印
while(leftUpRow <= rightLowRow) {
//从坐上角开始打印
int row = leftUpRow;
int col = leftUpCol;
//打印上面一行
while(col<=rightLowCol) { //为了防止最后一层最有一个数的情况,条件必须包含等于
res[index++] = matrix[row][col];
col++;
}
col--; //恢复,因为最后一次自增导致越界了
row++;
//打印右边一列
while(row<rightLowRow && index<res.length) {
res[index++] = matrix[row][col];
row++;
}
//打印下边一行
while(col>leftUpCol && index<res.length) {
res[index++] = matrix[row][col];
col--;
}
//打印左边一列
while(row>leftUpRow && index<res.length) {
res[index++] = matrix[row][col];
row--;
}
//打印完一层后,左上角和右下角都需要向中间移动一次
leftUpRow++; leftUpCol++;
rightLowRow--; rightLowCol--;
}
return res;
}
leetcode提交结果:
二、将0所在的行列清零
问题描述:如果矩阵中每一个元素为0,则将元素所在的行和列清零。
解题方法:首先扫描一遍数组,将其中元素为零的下标记录到一个辅助数组中。扫描完后再根据辅助数组中的记录进行清零。不能一边扫描一边进行清零,这会导致原本是不零的元素为零。
代码如下:
import java.util.Arrays;
public class 零所在的行列清零 {
public static void main(String[] args){
int[][] a = {{1,2,3,4},
{5,0,7,8},
{9,10,11,12},
{13,14,0,16}};
solve(a);
for(int i=0; i<a.length; i++){
System.out.println(Arrays.toString(a[i]));
}
}
private static void solve(int[][] a) {
int[] rowRecord = new int[a.length];
int[] colRecord = new int[a[0].length];
for(int i=0; i<a.length; i++){//扫描找出为零的元素
for(int j=0; j<a[0].length; j++){
if(a[i][j]==0){
rowRecord[i] = 1;
colRecord[j] = 1;
}
}
}
for(int i=0; i<a.length; i++){
for(int j=0; j<a[0].length; j++){
if(rowRecord[i]==1 || colRecord[j]==1){
a[i][j] = 0;
}
}
}
}
}
三、Z形打印矩阵
如下图所示:
解题方法:每次打印一条斜线,从左上角开始。打印斜线有两种情况:第一种是走下坡路,第二种是走上坡路。对于上坡路到最上面一行应该向右走一步,到最左一列应该向下走一步(上坡路只有这两种情况)。对于下坡路走到最下面一行应该向右走一步,到最左一列应该向下走一步(下坡路只有这两种情况)。
代码如下:
public class 按照Z字型打印矩阵 {
public static void main(String[] args){
int[][] a = {{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,16}};
printZ(a);
}
private static void printZ(int[][] a) {
int row = 0; int r = a.length;
int col = 0; int c = a[0].length;
boolean flag = true; //true表示上坡路,false表示下坡路
while(row<r && col<c){
if(flag){//走上坡路
System.out.print(a[row][col] + " ");
if(row==0 && col<c-1){//走到最上面的一行
col++;
flag = !flag;
continue;
}else if(col==c-1 && row<r-1){
//走到最右边一列
row++;
flag = !flag;
continue;
}else{//正在上坡
row--;
col++;
}
}else{//走下坡路
System.out.print(a[row][col] + " ");
if(col==0 && row<r-1){
//走到最左边的一列
row++;
flag = !flag;
continue;
}else if(row==r-1 && col<c-1){
//走到最下面的一行
col++;
flag = !flag;
continue;
}else{
//正在下坡
row++;
col--;
}
}
}
}
}
四、边界为1的最大子方阵
问题描述:给定一个N阶方阵,在这个方阵中,只有1和0两种值,返回边框全是1的最大正方形的边长长度。如下图:
解题方法:用枚举法,先假设最大正方形的边长是n(从n开始依次减一),然后去检测是否成立(依次遍历每一个元素,假设这个元素是最大正方形的左上角顶点,然后从这个顶点出发依次走遍四条边看是否全是1)。
代码如下:
public class 边界为1的最大子方阵 {
public static void main(String[] args){
int[][] a = {{0,1,1,1},
{0,1,0,1},
{0,1,1,1},
{0,1,0,1}};
System.out.println(solve(a));
}
private static int solve(int[][] a) {
int N = a.length;
int n = N;
int flag = 0;
while(n>0){
l2:for(int i=0; i<N; i++){
if(i+n>N) break;
l3:for(int j=0; j<N; j++){
if(j+n>N) break;
int row = i; int col = j;
while(col<j+n){//扫描上边
if(a[row][col++]==0) continue l3;
}
col--;
while(row<i+n){//扫描右边
if(a[row++][col]==0) continue l3;
}
row--;
while(col>=j){//扫描下边
if(a[row][col--]==0) continue l3;
}
col++;
while(row>i){//扫描左边
if(a[row--][col]==0) continue l3;
}
return n;
}
}
n--;
}
return n;
}
}
五、字数组最大累加求和
问题描述:给定一个数组,返回子数组的最大累加和(子数组是原数组连续的一部分)。
解题方法:
方法一:暴力法,计算出以每一个元素开头的所有子数组的最大累加和,然后进行比较得出总的最大累加和。时间复杂度为O(n^2)。代码省略。
方法二:开始将最大值假设为0,从第一个元素开始累加每加一次就和最大值进行比较,如果大于最大值就将最大值进行替换。如果累加的结果得到一个负数,就舍弃已经累加过的部分,以下一个元素为起点从新开始累加,同样的,每加一次都与最大值进行比较,如果大于就将最大值进行替换。(结果得到负数就舍弃的原因是因为一段连续的数字累加得到负数,说明最大累加和的子数组肯定不包含这一段)。时间复杂度为O(n)。
代码如下:
public class 子数组最大累加和 {
public static void main(String[] args){
int[] a = {1,-2,3,5,-2,6,-1};
System.out.println(findMax(a));
}
private static int findMax(int[] a) {
int max = a[0];
int sum = max;
for(int i=1; i<a.length; i++){
if(sum>0){
sum = sum + a[i];
}else{
sum = a[i];
}
if(sum > max){
max = sum;
}
}
return max;
}
}
六、子矩阵的最大累加和
问题描述:给定一个矩阵,返回子矩阵的最大累加和。
解题方法:暴力法就不说了,这题用到上一题的方法,上一题的方法是求一维数组的最大累加和,这题我们要做的就是将子矩阵转化为一维数组,起始行从第一行开始,算出第一行的最大子矩阵累加和,也就是一维数组的最大子数组的累加和(相当于将子矩阵的高固定为1)。之后再计算第一行到第二行的最大子矩阵累加和(相当于将子矩阵的高固定为2),以此类推。结束后,再将起始行放到第二行再进行一次计算。
代码如下:
import java.util.Arrays;
public class 子矩阵的最大累加和 {
public static void main(String[] args){
int[][] a = {{1,-2,3,5,-2,6,-1},
{3,-2,2,5,-2,6,2},
{1,-2,3,5,-2,6,-3},
{1,-2,3,5,2,6,-5},
};
System.out.println(findMatrix(a));
}
public static int findMatrix(int [][] a){
int beginrow = 0;
int r = a.length;
int c = a[0].length;
int[] sum = new int[c];
int max = 0;
while(beginrow<r){
for(int i=beginrow; i<r; i++){
//按列相加
for(int j=0; j<c; j++){
sum[j] += a[i][j];
}
int t = findMax(sum);
if(t>max) max = t;
}
Arrays.fill(sum, 0);//将数组清零,开始下一轮。
beginrow++;
}
return max;
}
private static int findMax(int[] a) {
int max = a[0];
int sum = max;
for(int i=1; i<a.length; i++){
if(sum>0){
sum = sum + a[i];
}else{
sum = a[i];
}
if(sum > max){
max = sum;
}
}
return max;
}
}
七、矩阵相乘
代码如下:
for(int i=0; i<n; i++){//m1的每一行
for(int j=0; j<p; j++){//m2的每一列
for(int k=0; k<m; k++){
result[i][j] = m1[i][k]*m2[k][j];
}
}
}