题目
给定一个N*N的矩阵matrix,在这个矩阵中只有0和1两种值,返回边框全是1的最大正方形的边长长度。
例如:
01111
01001
01001
01111
01011
其中,边框全是1的最大正方形的大小为4*4,所以返回4。
思路
暴力的方法是对于每个点,判断是否有边长为n,-1,n-2…的正方形图案,
因此复杂度为O(n*n)[共有n*n个点]*O(n)[遍历n个边长的正方形图案]*O(n)[判断该正方形图案是正方形],即O(n^4)
优化的地方在判断是否是正方形的地方,
添加2个二维数组,分别记录当前位置下边和右边1的个数
则判断是否是正方形只需判断左上角下和右1的个数、右上角下方1的个数、左下角右方1的个数
优化后为O(1)
即总的时间复杂度为O(n*n)[共有n*n个点]*O(n)[遍历n个边长的正方形图案]*O(1)[判断该正方形图案是正方形]+O(n*n)[初始化二维数组]=O(n^3)
例如right矩阵,有一行1 1 0 1 1,则right矩阵的值是2 1 2 2 1
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public static void setMap(int[][] m, int[][] down, int[][] right){ for(int i = m.length - 1; i >= 0; i--){ //最后一行 right[i][m[0].length - 1] = m[i][m[0].length - 1] == 1 ? 1 : 0; for(int j = m[0].length - 2; j >= 0; j-- ){ //主要看右边的 if(m[i][j+1]== 1){ right[i][j] = right[i][j+1] + m[i][j]; }else{ //当前值是0,就只加自己的值 right[i][j] = m[i][j]; } } } for(int i = m[0].length - 1; i >= 0; i--){ down[m.length - 1][i] = m[m[0].length - 1][i] == 1 ? 1 : 0; for(int j = m.length - 2; j >= 0; j-- ){ if(m[j+1][i]== 1){ down[j][i] = down[j+1][i] + m[i][j]; }else{ down[j][i] = m[j][i]; } } } } public static int getLen(int[][] m){ int[][] down = new int[m.length][m[0].length]; int[][] right = new int[m.length][m[0].length]; setMap(m, down, right); int len = 0; for(int i = 0; i < m.length; i++){ for(int j = 0; j < m[0].length; j++){ //k为生成的正方形边长 int x= Math.min(m.length - i, m[0].length - j); for(int k = x; k > 0; k--){ //左上角下和右1的个数、右上角下方1的个数、左下角右方1的个数 if(m[i][j] == 1 && right[i][j] >= k && down[i][j] >= k && right[i+k-1][j] >= k && down[i][j+k-1] >= k){ len = Math.max(k, len); } } } } return len; }
|
题目
给定一个整型矩阵matrix,其中的值只有0和1两种,求其中全是1的所有矩形区域中,最大的矩形区域为1的数量。
例如:
1110
其中,最大的矩形区域有 3 个 1,所以返回3。
再如:
1011
1111
1110
其中,最大的矩形区域有6个1,所以返回6。
思路
生成height[]
每行对应一个用height[],表示以当前行为底,连续的1的个数
然后把这些数字看成一个柱状图,找到最大的矩形面积,不断更新
找最大面积
这个找最大面积的过程是依次遍历每个数字,然后分别往左右两边扩,直到遇到比它小的数字就停止,
这个间距就是矩形的长,宽就是遍历的该数字。
此过程的时间复杂度是O(1),即遍历一次就能找到。
具体做法是生成一个栈,栈中存放数字的下标。
当栈空或者即将入栈的元素比栈顶大,就一直入栈。
直到遇到了比栈顶小的元素,此时开始出栈顶,那么出栈元素右边第一个比它小的就是当前元素,左边第一个比他小的就是新的栈顶元素。
这样就找到了每个矩形的面积,更新面积。
然后继续入栈出栈的过程。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public static int getMaxArea(int[][] map){ int[] height = new int[map[0].length]; int res = 0; //更新height for(int i = 0; i < map.length; i++){ for(int j = 0; j < height.length; j++){ //height记录的是连续的1的个数 height[j] = map[i][j] == 0 ? 0 : height[j] + 1; } res = Math.max(res, fArea(height)); } return res; } public static int fArea(int[] a){ //栈中存放数字的下标 Stack<Integer> stack = new Stack<Integer>(); int maxArea = 0; //实现一次遍历就找面积 for(int i = 0; i < a.length; i++){ //对于栈不空时,当前元素小于等于栈顶时都要出栈, while(!stack.empty() && a[i] <= a[stack.peek()]){ //栈顶出栈,得到矩形的宽a[k] int k = stack.pop(); //左边第一个比a[k]小的值 int left = stack.empty()? -1: stack.peek(); //右边第一个比a[k]小的值,同时注意存在相等的状况 int right = a[k] == a[i] ? i+1 : i; //区间是左开右开 int tmp = (right - left - 1)* a[k]; maxArea = Math.max(tmp, maxArea); } stack.push(i); } //遍历结束后仍然存在元素 while(!stack.empty()){ //栈顶出栈,得到矩形的宽a[k] int k = stack.pop(); //栈空时 int right = stack.empty()? a.length: k + 1; int left = stack.empty()? -1: stack.peek(); //区间是左开右开 int tmp = (right - left - 1)* a[k]; maxArea = Math.max(tmp, maxArea); } return maxArea; }
|