思路:二维前缀和
其实这道题和昨天的每日一题661.图片平滑器是一样的思路。
既然是周围的八个位置都需要考虑,我们就会想到用二维前缀和的加减法来实现,你也可以用模拟暴力实现,但是这样的话,我们需要讨论很多种情况,比如行为1的时候,列为1的时候,这个时候就需要处理很多的边界问题,所以我们选择用前缀和解决这个问题。
首先可以参考一下661.图片平滑器的模拟做法,时间复杂度是挺好的,但是就是在处理边界,以及特殊情况的时候,我们会感到极其的麻烦。
class Solution {
public int[][] imageSmoother(int[][] img) {
int res[][]=new int[img.length][img[0].length];
if(img.length==1&&img[0].length==1){
res[0][0]=img[0][0];
return res;
}
if(img.length==1&&img[0].length!=1){
for(int i=0;i<img[0].length;i++){
if(i==0){
int target=(img[0][i]+img[0][i+1])/2;
res[0][0]=target;
}
else if(i==img[0].length-1){
int target=(img[0][i]+img[0][i-1])/2;
res[0][img[0].length-1]=target;
}
else{
int target=(img[0][i]+img[0][i-1]+img[0][i+1])/3;
res[0][i]=target;
}
}
return res;
}
if(img.length!=1&&img[0].length==1){
for(int i=0;i<img.length;i++){
if(i==0){
int target=(img[i][0]+img[i+1][0])/2;
res[0][0]=target;
}
else if(i==img.length-1){
int target=(img[i][0]+img[i-1][0])/2;
res[img.length-1][0]=target;
}
else{
int target=(img[i][0]+img[i-1][0]+img[i+1][0])/3;
res[i][0]=target;
}
}
return res;
}
int m=img.length;
int n=img[0].length;
for(int i=0;i<img.length;i++){
if(i==0){
int av1=(img[0][0]+img[1][0]+img[0][1]+img[1][1])/4;
int av2=(img[0][img[0].length-1]+img[0][img[0].length-2]+img[1][img[0].length-1]+img[1][img[0].length-2])/4;
res[0][0]=av1;
res[0][img[0].length-1]=av2;
}
else if(i==img.length-1){
int av1=(img[i][0]+img[i][1]+img[i-1][0]+img[i-1][1])/4;
int av2=(img[i][img[0].length-1]+img[i][img[0].length-2]+img[i-1][img[0].length-1]+img[i-1][img[0].length-2])/4;
res[i][0]=av1;
res[i][img[0].length-1]=av2;
}
else{
int target=(img[i][0]+img[i][1]+img[i-1][0]+img[i-1][1]+img[i+1][0]+img[i+1][1])/6;
int target2=(img[i][n-1]+img[i][n-2]+img[i-1][n-1]+img[i-1][n-2]+img[i+1][n-1]+img[i+1][n-2])/6;
res[i][0]=target;
res[i][n-1]=target2;
}
}
for(int j=1;j<img[0].length-1;j++){
int target1=(img[0][j]+img[0][j-1]+img[0][j+1]+img[1][j+1]+img[1][j]+img[1][j-1])/6;
int target2=(img[m-1][j]+img[m-1][j+1]+img[m-1][j-1]+img[m-2][j+1]+img[m-2][j]+img[m-2][j-1])/6;
res[0][j]=target1;
res[m-1][j]=target2;
}
for(int i=1;i<img.length-1;i++){
for(int j=1;j<img[0].length-1;j++){
int target=(img[i][j]+img[i][j+1]+img[i][j-1]+img[i+1][j]+img[i+1][j-1]+img[i+1][j+1]+img[i-1][j]+img[i-1][j-1]+img[i-1][j+1])/9;
res[i][j]=target;
}
}
return res;
}
}
回到本题,接下来就是介绍前缀和的做法:
比如我们在(i,j)这个坐标上,我们的周围八个位置把我们包围成一个3x3的矩阵,其实说实话,就是右下角的坐标以及左上角的坐标包含的一个区域罢了。看到这里,我们可以想到,这里用前缀和其实是划算的,我们直接求出右下角的前缀和,然后再减去两边的前缀和,加上减去的多余的前缀和,最后再减去自身的值,这样的话,我们就求出来了这个坐标周围的细胞总和是多少,然后按照题目要求直接判断就行了。
对了,因为可能会有边界问题,我们在进行计算的时候对边界的坐标进行了处理,以防止数组越界。
关于二维前缀和,可以去百度一下,知乎,csdn都有详细的介绍。
class Solution {
public void gameOfLife(int[][] board) {
int m=board.length;
int n=board[0].length;
int res[][]=new int[m][n];
int [][]sum=new int[m+1][n+1];
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+board[i-1][j-1];
}
}
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
int x1=Math.min(m-1,i+1);
int y1=Math.min(n-1,j+1);
int x2=Math.max(0,i-1);
int y2=Math.max(0,j-1);
int t=sum[x1+1][y1+1]-sum[x1+1][y2]-sum[x2][y1+1]+sum[x2][y2]-board[i][j];
if(board[i][j]==0){
if(t==3){
res[i][j]=1;
}
}
else{
if(t<2){
res[i][j]=0;
}
else if(t==2||t==3){
res[i][j]=1;
}
else if(t>3){
res[i][j]=0;
}
}
}
}
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
board[i][j]=res[i][j];
}
}
}
}