首页 > 编程语言 >【算法】【优选算法】前缀和(下)

【算法】【优选算法】前缀和(下)

时间:2024-11-24 14:01:03浏览次数:11  
标签:优选 前缀 nums int ret 算法 数组 dp

目录


一、560.和为K的⼦数组

题目链接:560.和为K的⼦数组
题目描述:

题目解析:

  • 求数组中子串的和为k的个数。

1.1 前缀和

解题思路:

  • 假设已经创建好了一个前缀和数组dp,我们使用前缀和的时候,判断从0到 i 位置的和为k的子数组个数,只需要在dp下标[ 0 , i - 1 ]中找dp元素值为dp[ i ] - k的个数即可。
  • 所以我们使用一个容器hash表来存储从0 到 i - 1的前缀和的个数,关键字key就是前缀和,values就是次数。
  • 细节处理:
    • 我们不需要真的使用前缀和数组,只需要遍历原数组时,用一个变量记录遍历过的元素和即可。
    • 当该前缀和就是k的时候,我们上面条件没有考虑,所以我们还要先放入(0,1)表示这种情况。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(n)
class Solution {
    public int subarraySum(int[] nums, int k) {
        Map<Integer,Integer> hash = new HashMap<>();
        hash.put(0,1);
        int sum = 0;
        int ret = 0;
        for(int i = 0; i < nums.length; i++) {
            sum += nums[i];
            ret += hash.getOrDefault(sum-k,0);
            hash.put(sum, hash.getOrDefault(sum,0)+1);
        }
        return ret;
    }
}

1.2 暴力枚举

解题思路:

  • 直接使用两层for循环,将每一种可能枚举出来。

解题代码:

//时间复杂度:O(n^2)
//空间复杂度:O(1)
class Solution {
    public int subarraySum(int[] nums, int k) {
        int ret = 0;
        for(int i = 0; i < nums.length; i++) {
            int sum = 0;
            for(int j = i; j < nums.length; j++) {
                sum += nums[j];
                if(sum == k) {
                    ret++;
                }
            }
        }
        return ret;
    }
}

二、974.和可被K整除的⼦数组

题目链接:974.和可被K整除的⼦数组
题目描述:

题目解析:

  • 跟上一道题一样的思路,只不过这个是求能被整除的个数而已。

2.1 前缀和

解题思路:

  • 同余定理:如果(a - b)% p == 0 那么a % p 和b % p值相等。
  • Java中负数对正数取余修正:在Java中负数对正数取余余数会是负数,修正方法就是:(a % p + p)% p
  • 使用hash表将i下标前的每一个前缀和与k的余数存入。
  • 再看前面前缀和与当前 前缀和的余数相同的个数即可。
  • 当[0 , i]本身前缀和余数为0的时候,就是一个符合条件的子数组。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(n)
class Solution {
    public int subarraysDivByK(int[] nums, int k) {
        int ret = 0;
        Map<Integer,Integer> hash = new HashMap<>();
        hash.put(0 % k , 1);
        int sum = 0;
        for(int x : nums) {
            sum += x;
            int key = (sum % k + k ) % k;
            ret += hash.getOrDefault(key,0);
            hash.put(key, hash.getOrDefault(key , 0) + 1);
            
        }
        return ret;
    }
}

2.2 暴力枚举

解题思路:

  • 直接遍历数组,在将遍历元素的和取余即可。
  • 会超时。

解题代码:

//时间复杂度:O(n^2)
//空间复杂度:O(1)
class Solution {
    public int subarraysDivByK(int[] nums, int k) {
        int ret = 0;
        for(int i = 0; i < nums.length; i++) {
            int sum = 0;
            for(int j = i; j < nums.length; j++) {
                sum += nums[j];
                if((sum % k + k) % k == 0) ret++;
            }
        }
        return ret;
    }
}

三、525.连续数组

题目链接:525.连续数组
题目描述:

题目解析:

  • 要我们返回子数组中 0 和1 数量相等的最长子数组的长度。

3.1 前缀和

解题思路:

  • 我们使用一个容器hash表,关键字key来记录原数组每个下标i中的1与0个数差,而values记录这个差值的最小下标。
  • 注意边界情况,如果刚好整个数组满足条件,结果就是数组长 又等于nums.length-1 + 1所以我们初始一个(0,-1)
  • 求长度的时候,我们在前面找到 j 下标与现在 i 下标关键字一样,那么数组区间就是[ j+1 , i ]

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(n)
class Solution {
    public int findMaxLength(int[] nums) {
        int ret = 0;
        int n = nums.length;
        Map<Integer,Integer> hash = new HashMap<>();
        hash.put(0,-1);
        //前面1和0个数之差
        int num = 0;
        for(int i = 0; i < n; i++) {

            if(nums[i] == 0) num--;
            else num++;

            if(hash.containsKey(num)) ret = Math.max(ret, i - hash.get(num));
            else hash.put(num, i);
        }
        return ret;
    }
}

3.2 暴力枚举

解题思路:

  • 两层for循环遍历数组,记录每一个子数组中1和0的个数,
  • 当个数相同的时候,更新结果。
  • 会超时

解题代码:

//时间复杂度:O(n^2)
//空间复杂度:O(1)
class Solution {
    public int findMaxLength(int[] nums) {
        int ret = 0;
        for(int i = 0; i < nums.length; i++) {
            int oneNum = 0;
            int zeroNum = 0;
            for(int j  = i; j < nums.length; j++) {
                if(nums[j] == 0) zeroNum++;
                else oneNum++;

                if(oneNum == zeroNum) ret = Math.max(ret,j-i+1);
            }
        }
        return ret;
    }
}

四、1314.矩阵区域和

题目链接:1314.矩阵区域和
题目描述:

题目解析:

  • 给一个二维数组,给一个k,返回的二维结果数组中数组( i , j )下标的值是原数组( i-k , j-k )到( i+k , j+k)的和。
  • 就像下图中红方框框起来的:

4.1 前缀和

解题思路:

  • 其实着就是前缀和上篇中给出的二维前缀和模版。
  • 我们使用一个二维数组dp比原来数组多一行一列,dp[ i ][ j ]就是原数组中(0 , 0)到(i - 1 , j -1)的元素和。
  • dp[ i ][ j ] = dp[ i - 1][j - 1] + nums[ i - 1][j - 1]。
  • 在结果数组中与原数组大小一样,本来是求原数组( i-k , j-k )到( i+k , j+k)的和。那么对应到dp数组中,都要加1。
  • 注意越界,如果( i-k , j-k )小于0那么就是0,i+k大于原数组行数,那么就是原数组行数,j+k大于原数组列数,那么就是原数组列数。

解题代码:

//时间复杂度:O(n^2)
//空间复杂度:O(n^2)
class Solution {
    public int[][] matrixBlockSum(int[][] mat, int k) {
        int n = mat.length;
        int m = mat[0].length;
        int[][] dp = new int[n+1][m+1];
        dp[0][0] = mat[0][0];
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                    dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + mat[i-1][j-1];
            }    
        } 
        int[][] ret = new int[n][m]; 
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                int x2 = i+k > n-1 ? n-1 : i+k;
                int y2 = j+k > m-1 ? m-1 : j+k;
                int x1 = i-k < 0 ? 0 : i-k;
                int y1 = j-k < 0 ? 0 : j-k;
                ret[i][j] = dp[x2+1][y2+1] - dp[x2+1][y1-1+1] - dp[x1-1+1][y2+1]+dp[x1-1+1][y1-1+1];
            }
        }
        return ret;
    }
}

4.2 暴力枚举

解题思路:

  • 先两层for循环,拿到结果数组行列,
  • 再两层for循环,求原数组( i-k , j-k )到( i+k , j+k)的和。

解题代码:

//时间复杂度:O(n^4)
//空间复杂度:O(1)
class Solution {
    public int[][] matrixBlockSum(int[][] mat, int k) {
        int n = mat.length;
        int m = mat[0].length;
        int[][] ret = new int[n][m];
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                int x2 = i+k > n-1 ? n-1 : i+k;
                int y2 = j+k > m-1 ? m-1 : j+k;
                int x1 = i-k < 0 ? 0 : i-k;
                int y1 = j-k < 0 ? 0 : j-k;
                for(int w = x1; w <= x2; w++) {
                    for(int q = y1; q <= y2; q++) {
                        ret[i][j] += mat[w][q];
                    }
                }
            }
        }
        return ret;
    }
}

标签:优选,前缀,nums,int,ret,算法,数组,dp
From: https://blog.csdn.net/yj20040627/article/details/143570391

相关文章

  • 每日一练:【优先算法】双指针之快乐数(medium)
    1.题目链接:202.快乐数2.题目描述及分析对于一个正整数我们替换为它每个位置上数字的平方和,不断重复这个过程就如上图所示。这里需要补充的是根据鸽巢定理,n个巢穴,n+1个鸽子,,将鸽子都安排进巢穴,那么不管怎么安排,至少有一个有一个巢穴里面鸽数大于1,我们这里取一个超过int......
  • 代码随想录算法训练营第二十五天|LeetCode491.递增子序列、46.全排列、47.全排列II、3
    前言打卡代码随想录算法训练营第49期第二十五天  ○(^皿^)っHiahiahia…首先十分推荐学算法的同学可以先了解一下代码随想录,可以在B站卡哥B站账号、代码随想录官方网站代码随想录了解,卡哥清晰易懂的算法教学让我直接果断关注,也十分有缘和第49期的训练营大家庭一起进步。今......
  • 图论最短路算法笔记
    1图的基本操作1.1图的存储图存储有两种方法:邻接表和邻接矩阵。邻接表:g[N][N]={};...memset(g,0x3f,sizeofg);g[u][v]=w;邻接矩阵:inthead[N]={};memset(head,0x3f,sizeofhead);structedge{intpre,to,val;}EDGE[N];inlinevoidaddedge(......
  • 字节 NLP 算法岗一面面试题7道(含解析)
    最近这一两周不少互联网公司都已经开始秋招提前批面试了。不同以往的是,当前职场环境已不再是那个双向奔赴时代了。求职者在变多,HC在变少,岗位要求还更高了。最近,我们又陆续整理了很多大厂的面试题,帮助一些球友解惑答疑,分享技术面试中的那些弯弯绕绕。总结如下:《大模型面......
  • 【数据集】【YOLO】【目标检测】牛只识别数据集 3371 张,YOLO牛只识别算法实战训练教程
     一、数据集介绍【数据集】牛只识别数据集3371张,目标检测,包含YOLO/VOC格式标注。数据集中包含1种分类:names:['cattle'],表示"牛"。数据集来自国内外网站图片采集、无人机采集、监控视频采集数据,含三种视角!!如下所示;可用于无人机牛只识别,监控牛只识别。检测场景为牧场、......
  • 用python写一段k-means聚类算法,要求使其能够显示聚类前后的差异,绘图使其可视化
    当您使用K-means算法时,可以使用scikit-learn库中的KMeans类来实现。以下是一个示例代码,可以帮助您理解如何使用K-means算法进行聚类,并使用matplotlib库绘制可视化结果。importnumpyasnpfromsklearn.clusterimportKMeansimportmatplotlib.pyplotasplt#创建一个......
  • C++,Java,Python,Javascript实现二分查找算法
    二分查找算法是一种在有序数组中查找特定元素的搜索算法。它的基本思想是将数组分成两半,通过比较中间元素与目标值来决定是在左半部分还是右半部分继续查找,从而逐步缩小查找范围直到找到目标值或者确定目标值不存在于数组中。下面是使用C++、Java、Python和JavaScript实现二......
  • Floyd算法计算最短距离
     importcom.google.common.collect.HashBasedTable;importcom.google.common.collect.Table;importjava.util.Arrays;publicclassApp{//正无穷publicstaticdoubleinfinity=Double.POSITIVE_INFINITY;/***计算所有节点最短路径的Floyd......
  • 《链表算法:浅谈并实现一下链表各种排序算法及其性能》
    前置知识数据结构-链表数组排序算法:选择排序[解法1],归并排序递归版[解法2],归并排序迭代法[解法3最优解][归并部分基础]合并两个有序链表如果您不满足于此,笔者也提供冒泡排序,插入排序,快速排序的链表写法。以及,我们会在下文讨论为什么不说明希尔排序,堆排序,因为两者不适合......
  • 多种智能优化算法优化极致梯度提升算法(XGBoost)的数据回归预测
    极致梯度提升算法(XGBoost)是一种非常高效的梯度提升框架,广泛应用于监督学习任务,特别是在数据回归预测中。尽管XGBoost通过自动调节参数和剪枝等技术已经具有很强的性能,但通过多种智能优化算法进一步优化其参数,可以显著提升其在数据回归预测任务中的表现。代码原理及流程1.XGBo......