20-5-8 LC(正方形/最长序列和)

LC每日一题

  1. 最大正方形 在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。

思路

dp[i][j]表示以matrix[i][j]为右下角的正方形的最大边长

代码

class Solution {
    public int maximalSquare(char[][] matrix) {
        if(matrix==null||matrix.length==0||matrix[0].length==0)return 0;
        int rows=matrix.length;
        int cols=matrix[0].length;
        int[][] dp=new int[rows][cols];
        int max=0;
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                if(matrix[i][j]=='1'){
                    if(i==0||j==0)dp[i][j]=1;
                    else dp[i][j]=Math.min(dp[i][j-1],Math.min(dp[i-1][j],dp[i-1][j-1]))+1;
                }
                max=Math.max(max,dp[i][j]);
            }
        }
        return max*max;
    }
}

1277. 统计全为 1 的正方形子矩阵

给你一个 m * n 的矩阵,矩阵中的元素不是 0 就是 1,请你统计并返回其中完全由 1 组成的 正方形 子矩阵的个数。

思路

和上题一样,只不过只要最小边不是1的话,就根据边的大小来计算有多少种正方形

class Solution {
    public int countSquares(int[][] matrix) {
        if(matrix==null||matrix.length==0||matrix[0].length==0)return 0;
        int rows=matrix.length;
        int cols=matrix[0].length;
        int[][] dp=new int[rows][cols];
        int ans=0;
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                if(matrix[i][j]==1){
                    if(i==0||j==0){
                        dp[i][j]=1;
                        ans++;
                    }else{
                        dp[i][j]=Math.min(Math.min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1;
                        if(dp[i][j]!=1){
                            ans+=dp[i][j];
                        }else ans++;
                    }
                }
            }
        }
        return ans;
    }
}

85. 最大矩形

给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

思路

矩形不能像正方形那样通过左上左上角的值来求边长,所以就采用对每一列依次往上求面积,同时保存每行的最小值 dp[i][j]表示第i行第j列左边的最长边长多少。然后以j列为基础依次往上计算,取边长最小值*高。高是每次加1的,相当于i到i-1到i-2这样

代码

class Solution {
    public int maximalRectangle(char[][] matrix) {
        if(matrix.length==0)return 0;
        int rows=matrix.length;
        int cols=matrix[0].length;
        int[][] dp=new int[rows][cols];
        int max=0;
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                if(matrix[i][j]=='1'){
                    if(j==0)dp[i][j]=1;
                    else dp[i][j]=1+dp[i][j-1];
                    int width=dp[i][j];
                    for(int k=i;k>=0;k--){
                        width=Math.min(width,dp[k][j]);
                        max=Math.max(max,width*(i-k+1));
                    }
                }
            }
        }
        return max;
    }
}

84. 柱状图中最大的矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。有点类似接雨水

思路

1.暴力就是用一个min保存到当前为止的最小值,O(n^2),然后求当前坐标前面的所有可能。 2.分治就是找到最小值求面积,然后对最小值左边的区域递归求面积,右边区域也递归求面积 3.最小值队列就是维护一个到i位置的最小值队列,每当到一个新的位置,如果小于队尾,就将队尾位置弹出,代表这个位置的高度已经可求了(同时要弹出栈里相同值的位置),然后就分情况,看栈空不空来决定左界,右界是index 4.可用哨兵来优化,最小队列,就是在原数组的头尾分别放入一个哨兵0,选0是因为要保证哨兵是最小值,这样队列就永远非空,不用做空判断,且尾哨兵会将队中剩余的递增元素从尾巴全部弹出,不需要两次迭代

分治

class Solution {
        public int largestRectangleArea(int[] heights) {
            if(heights==null||heights.length==0)return 0;
            return max(0,heights.length-1,heights);
        }
        int max(int low,int high,int[] heights){
            if(low>high)return 0;
            int min=low;
            for(int i=low;i<=high;i++)
                if(heights[i]<heights[min])
                    min=i;
            int max=heights[min]*(high-low+1);
            int left=max(low,min-1,heights);
            int right=max(min+1,high,heights);
            return Math.max(Math.max(max,left),right);
        }
    }

最小值队列/单调栈

class Solution {
    public int largestRectangleArea(int[] heights) {
        if(heights==null||heights.length==0)return 0;
        Deque<Integer> queue=new LinkedList<>();
        int max=0;
        int index=0;
        while(index<heights.length){
            while(!queue.isEmpty()&&heights[index]<heights[queue.peekLast()]){
                int tmp=queue.pollLast(); //此处如果队列有和弹出相同值的位置不管,因为最终会被弹出,且结果才是正确的
                if(!queue.isEmpty())
                    max=Math.max(max,(index-queue.peekLast()-1)*heights[tmp]);
                else 
                    max=Math.max(max,index*heights[tmp]);
            }
            queue.offer(index++);
        }
        while(!queue.isEmpty()){
            int tmp=queue.pollLast();
            if(queue.isEmpty())max=Math.max(max,heights[tmp]*heights.length);
            else max=Math.max(max,heights[tmp]*(heights.length-queue.peekLast()-1));  
        }
        return max;
    }
}

最小值队列加上哨兵法,就是扩容两个位置,同尾放入的是可取的最小值

class Solution {
    public int largestRectangleArea(int[] heights) {
        if(heights==null||heights.length==0)return 0;
        if(heights.length==1)return heights[0];
        Deque<Integer> queue=new LinkedList<>();
        int[] h=new int[heights.length+2];
        h[0]=0;h[heights.length+1]=0;
        System.arraycopy(heights,0,h,1,heights.length);
        int max=0;
        int index=1;
        queue.offer(0);
        while(index<h.length){
            while(h[index]<h[queue.peekLast()])
                max=Math.max(max,h[queue.pollLast()]*(index-queue.peekLast()-1));
            queue.offer(index++);
        }
        return max;
    }
}

560. 和为K的子数组

给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。该题数组可以取负值,且无序,所以不能单纯使用双指针加前缀和

思路

因为该题为无序数组,且存在负值,所以必须用前缀和+哈希。 单纯的双指针只能用在非负的无序数组,一旦存在负数就没办法靠移动头尾指针来加减和,因为本身双指针就是前缀和的思想。 所以此题用前缀和,每次遍历到一个新数就求和,然后看是否=k,等于k的话先+1,再去看是否之前的前缀和中存在0(通过map),因为sum[i]-sum[j]=k,0...j...i...nums.length-1

class Solution {
    public int subarraySum(int[] nums, int k) {
        if(nums==null||nums.length==0)return 0;
        HashMap<Integer,Integer> map=new HashMap<>();
        int sum=0,ans=0;
        for(int i=0;i<nums.length;i++){
            sum+=nums[i];
            if(sum==k)ans++;
            if(map.containsKey(sum-k)){
                ans+=map.get(sum-k);
            }
            map.put(sum,map.getOrDefault(sum,0)+1);
        }
        return ans;
    }
}
原文地址:https://www.cnblogs.com/k-will/p/12853550.html