leetcode之前缀和刷题总结1

这篇博客总结了LeetCode中使用前缀和与双指针解决数组问题的五道经典题目,包括求最小子数组、除自身外的数组乘积、连续数组、连续子数组和、子数组和为K的情况。通过这些题目展示了如何巧妙运用这两种技巧降低时间复杂度,提高解题效率。
摘要由CSDN通过智能技术生成

leetcode之前缀和刷题总结1

1-长度最小的子数组
题目链接:题目链接戳这里!!!

思路:前缀和+二分查找
求出数组的前缀和,在前缀和中二分查找并更新最小的子数组

AC代码如下:

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length ;
        int [] sums = new int [n+1] ;
     
        int min = Integer.MAX_VALUE ;
        for(int i=1; i<=n; i++){ //前缀和
            sums[i] = sums[i-1]+nums[i-1];
        }
        for(int i=1; i<=n; i++){//二分查找
            int s = target + sums[i-1] ;
            int bound = Arrays.binarySearch(sums,s) ;
            if(bound<0){
                bound = -bound - 1 ;
            }
            if(bound<=nums.length){
                min = Math.min(min,bound-(i-1)) ;
            }
        }
        return min==Integer.MAX_VALUE ? 0 : min ;
    }
}

在这里插入图片描述
方法2:双指针法

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length ;
        int min = Integer.MAX_VALUE ;
        int sum = 0 ;
        int left=0, right = 0 ;
        while(right<n ){
          sum += nums[right] ;
          while(sum>=target){
              min = Math.min(min, right-left+1) ;
              sum -= nums[left] ;
              left++ ;
          }
          right++ ;
        }
        return Integer.MAX_VALUE==min ? 0 : min ;
    }
}

2-除自身以外得数组得乘积
题目链接:题目链接戳这里!!!

AC代码如下:

思路:就是求出每个值得左侧累乘乘以右侧累乘。

class Solution {
    public int[] productExceptSelf(int[] nums) {
        //每个值得左侧累乘值乘以右侧累乘值
        int [] res = new int [nums.length] ;
        int left=1, right=1 ;
        Arrays.fill(res,1) ;
        for(int i=0; i<nums.length; i++){
            res[i] *= left ;
            left *= nums[i] ;
            res[nums.length-i-1] *= right;
            right *= nums[nums.length-1-i] ;
            
        }
        return res ;
    }
}

在这里插入图片描述
这样写看起来更容易理解,效率也更高了。

class Solution {
    public int[] productExceptSelf(int[] nums) {
        //每个值得左侧累乘值乘以右侧累乘值
        int [] res = new int [nums.length] ;
        int left=1, right=1 ;

        for(int i=0; i<nums.length; i++){
            res[i] = left ;
            left *= nums[i] ;
        }
        for(int i=nums.length-1; i>=0; i--){
            res[i] *= right ;
            right *= nums[i] ;

        }
        return res ;
    }
}

在这里插入图片描述
3-连续数组
题目链接:题目链接戳这里!!!

思路:前缀和+哈希表,将数组0都变为1,就变成了求前缀和为0得连续数组得最大长度。

AC代码如下:

class Solution {
    public int findMaxLength(int[] nums) {
        Map<Integer,Integer> map = new HashMap<>() ;
        int n = nums.length ;
        for(int i=0; i<n; i++){
            if(nums[i]== 0){
                nums[i] = -1 ;
            }
        }

        int sum = 0, temp = 0 ;
     
        for(int i=0; i<n; i++){
            sum += nums[i] ;//前缀和
            if(sum==0){//前缀和为0
                temp = i+1;
            }
            if(map.containsKey(sum)){//前缀和为0
                temp = Math.max(i-map.get(sum),temp) ;
            }else{
                map.put(sum, i) ;
            }
            
        }
        return temp ;
    }
}

在这里插入图片描述
4-连续得子数组和
题目链接:题目链接戳这里!!!

思路:前缀和+哈希表
如果前缀和对k取余为0,且数组长度为2以上,则为真
如果前缀和相等,且数据长度为2以上,则为真,
否则为假。

AC代码如下:

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        //前缀和+哈希表
        int n = nums.length ;
        if(n<2){
            return false ;
        }
        int sum = 0 ;
        Map<Integer, Integer> map = new HashMap<>() ;
        for(int i=0; i<n; i++){
            sum += nums[i] ;
            int temp = sum % k ;
            if(i>=1 && temp==0){//前缀和对k取余等于0
                return true ;
            }
            if(map.containsKey(temp)){ //前缀和对k取余相等且下标大于等于2则符合
                int preIndex = map.get(temp) ;
                if(i-preIndex>=2){
                    return true ;
                }
            }else{
                map.put(temp, i) ;
            }
        }
        return false ;
    }
}

在这里插入图片描述
5-和为K得子数组
题目链接:题目链接戳这里!!!

思路1:直接用前缀和统计,这样得话需要双重循环,时间复杂度较高,不过这题的测试用例O(n^2)的复杂度也可以通过

AC代码如下:

class Solution {
    public int subarraySum(int[] nums, int k) {
        int n = nums.length ;
        int [] sums = new int [n] ;
        sums[0] = nums[0] ;
        for(int i=0; i<n-1; i++){
            sums[i+1] = sums[i] + nums[i+1] ;
        }
        int cnt = 0 ;
        for(int i=0; i<n; i++){
            if(sums[i]==k){
                cnt ++ ;
            }
            for(int j=i+1; j<n; j++){
                if(sums[j]-sums[i]==k){
                    cnt ++ ;
                }
            }
        }
        return cnt ;
    }
}

在这里插入图片描述
还是用前缀和+哈希表,把时间复杂度降为O(n)
AC代码如下:

class Solution {
    public int subarraySum(int[] nums, int k) {
        int n = nums.length ;
        Map<Integer, Integer> map = new HashMap<>() ;
        int cnt = 0,sum = 0  ;
        for(int i=0; i<n; i++){
           sum += nums[i] ;
           if(sum==k){
               cnt ++ ;
           }
           if(map.containsKey(sum-k)){
              cnt += map.get(sum-k) ;
           }
           map.put(sum, map.getOrDefault(sum, 0)+1) ;
        }
        return cnt ;
    }
}

在这里插入图片描述
6-最大连续1的个数III
题目链接:

思路:双指针,也叫滑动窗口。

class Solution {
       public int longestOnes(int[] A, int K) {
       int left=0, right=0;//窗口左右侧
       int max = 0 ;//最大窗口
       int zero=0 ;//窗口中0的个数
       for(;right<A.length; right++){
           if(A[right]==0){
               zero++ ;
           }
           while(zero>K){
               if(A[left++]==0){
                   zero-- ;
               }
           }
           max = Math.max(max, right-left+1) ;
       }
       return max ;
    }
}

在这里插入图片描述
不需要while循环的滑动窗口

class Solution {
       public int longestOnes(int[] A, int K) {
       int left=0, right=0;//窗口左右侧
       int zero=0 ;//窗口中0的个数
       for(;right<A.length; right++){
           if(A[right]==0){
               zero++ ;
           }
           if(zero>K){
               zero -= (1 - A[left++]) ;
           }
       }
       return right - left ;
    }
}

在这里插入图片描述
7-寻找数组的中心下标
题目链接:题目链接戳这里!!!

思路:判断左右端的前缀和是否相等即可。

AC代码如下:

class Solution {
    public int pivotIndex(int[] nums) {
        int n = nums.length ;
        int sum = 0 ;
        for(int i=1; i<n; i++){ //从第2个开始求和等于0,则第一个是中心
            sum += nums[i] ;
        }
        if(sum==0){
            return 0 ;
        }
        int [] sums = new int [n] ;
        sums[0] = nums[0] ;
        for(int i=0; i<n-1; i++){ //求前缀和
            sums[i+1] = sums[i] + nums[i+1] ; 
        }
        for(int j=0; j<n; j++){ //右侧前缀和等于左侧则找到了
            if(j+1<n && sums[j] == (sums[n-1]-sums[j+1])){
                return j+1 ;
            }
        }
        return -1 ;
    }
}

在这里插入图片描述
8-和相同的二元子数组
题目链接:题目链接戳这里!!!

思路:前缀和+哈希表
每次记录前缀和,找到所有前缀和值为goal的。

AC代码如下:

class Solution {
    public int numSubarraysWithSum(int[] nums, int goal) {
       //前缀和+hash表
       int sum = 0, cnt = 0; 
       Map<Integer, Integer> map = new HashMap<>() ;
       for(int i=0; i<nums.length; i++){
            sum += nums[i] ;
           if(sum==goal){
               cnt ++  ;
           }
           if(map.containsKey(sum-goal)){
               cnt += map.get(sum-goal) ; 
           }
            map.put(sum, map.getOrDefault(sum,0)+1) ;
       }
       return cnt ;
    }
}

在这里插入图片描述
意难平终将和解,万事终将如意,加油吧,少年。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nuist__NJUPT

给个鼓励吧,谢谢你

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值