Java前缀和算法

这篇具有很好参考价值的文章主要介绍了Java前缀和算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.什么是前缀和算法

通俗来讲,前缀和算法就是使用一个新数组来储存原数组中前n-1个元素的和(如果新数组的当前元素的下标为n,计算当前元素的值为原数组中从0到n-1下标数组元素的和),可能这样讲起来有点抽象,我们举一个例子对其进行说明:给定一个数组nums[],我们创建一个大小为nums.length+1的求和数组sums[],对sum[]数组进行遍历:sum[i]=nums[0]+nums[1]+...+nums[i-1](相当于nums[i-1]+sum[n-1]),比如存在int[]nums={1,2,3,4},对应的前缀和数组的值为sum={0,1,3,6,10}

二.前缀和算法相对于滑动窗口具有哪些不可替代的优势及前缀和算法对算法题目的优化策略

 我们在前面讲述的前缀和算法,可能很多小伙伴对其的特点也有所察觉:发现其与我们上一节讲到的滑动窗口有些类似,我们结合下面这道题目,讲讲前缀和算法对于如下这种题目而言,具有怎样的解题优势

Java前缀和算法

对于滑动窗口一般的解题思路如下:如果当前窗口的元素大于预期条件的元素时,我们将窗口的左指针向右移动;如果当前窗口的元素小于预期条件的元素时,我们将窗口的右指针向右移动,扩大窗口,但是对于上面的题目中,测试用例中存在负数的情况,那么对于窗口中出现了负数这种情况,我们是该将窗口右移直到满足条件还是将窗口左移直到剔除这个负数呢?这明显超出了我们之前规定的对于使用滑动窗口的条件,这也就是滑动窗口的局限性,而根据我们对于上文中对前缀和算法的描述,前缀和算法并不规定其中的元素一定要是正数或者负数,我们只是将其中的元素进行相加,而前缀和算法对于解决这种题目可谓大显身手。

除此之外,我们讲一下前缀和算法的应用相对于我们使用暴力解法具有怎样的优化:如果不创建一个新的数组来存储前n个元素的和,那么此时我们每次求和时,都需要从头到尾将元素和全部求一遍,对于一维数组,时间复杂度达到了O(n),但是如果我们将前面元素和进行存储,每次求和只需要调用我=我们前缀和数组中的元素,时间复杂度只有O(1),大大降低了时间复杂度;同理,对于二维数组而言,他可以将O(n^2)的时间复杂度降至O(1).

三.前缀和算法的模板代码

对于前缀和算法,一般存在一维的前缀和与二维的前缀和这样两种不同的算法模板,关于前缀和算法的模板,我们使用preSum来计算数组的前缀和,preSum[i]代表着前i个前i-1个数组元素的和;对于二维数组,preSum[i][j]则代表着行为 0到i-1,列为0-j-1元素的和

一维前缀和模板

如下是一维前缀和算法的模板代码:

Java前缀和算法

    public int[] preSum(int[] nums) {
        int[] preSum = new int[nums.length+1];
        for (int i = 1; i < =nums.length; ++i) {
            preSum[i] = preSum[i - 1] + nums[i-1];
        }
        return preSum;
 
    }

二维前缀和模板

如下是二维前缀和的算法模板:对于二维的前缀和模板,是对一维前缀和模板的扩展,但在本质上是完全一致的,我们来描述有关二维数组的前缀和的模板:

Java前缀和算法

我们给出这样一个图示:
Java前缀和算法

对于二维数组的前缀和而言,表达式可以表述为这样:

每一个二维数组中的单位元素,相等于绿-澄-蓝+粉,我们同样给出一个二维数组,presum[nums.length+1][nums[0].length+1],presum[n][n]则代表0-n-1行,0-n-1列的数据总和 、

其模板表达式如下:

class NumMatrix {
    // 定义:preSum[i][j] 记录 matrix 中子矩阵 [0, 0, i-1, j-1] 的元素和
    private int[][] preSum;
    
    public NumMatrix(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        if (m == 0 || n == 0) return;
        // 构造前缀和矩阵
        preSum = new int[m + 1][n + 1];
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                // 计算每个矩阵 [0, 0, i, j] 的元素和
                preSum[i][j] = preSum[i-1][j] + preSum[i][j-1] + matrix[i - 1][j - 1] - preSum[i-1][j-1];
            }
        }
    }
    
    // 计算子矩阵 [x1, y1, x2, y2] 的元素和
    public int sumRegion(int x1, int y1, int x2, int y2) {
        // 目标矩阵之和由四个相邻矩阵运算获得
        return preSum[x2+1][y2+1] - preSum[x1][y2+1] - preSum[x2+1][y1] + preSum[x1][y1];
    }
}

除了完全的一维和二维的前缀和算法之外,前缀和算法的题目通常会与hash等数据结构组合出题

四.关于前缀和算法的经典题目练习

1.区域和检索 - 数组不可变力扣

力扣题目描述

Java前缀和算法

解题思路

这是一个完全意义上的前缀和算法的题目,或者说这是一个书写前缀和算法模板的题目,对于这道题,我们直接使用我们前面使用的前缀和算法的模板即可:创建nums.length+1长度的presum[],对于presum[n],存储的是前n-1个元素的和,每次求和时直接使用presum中的元素即可。

实例代码文章来源地址https://www.toymoban.com/news/detail-459334.html

我们给出实例代码:
 

class Solution {
    public int subarraySum(int[] nums, int k) {
        //单纯使用前缀和,无法判别从哪里进行区分,这时我们需要将符合条件的数值直接存入map,再次需要时直接拿来使用即可
        int count=0;//记录最终的结果
        int pre=0;//记录前缀和
        //创建map
        HashMap<Integer,Integer>map=new HashMap<>();
        //如果是pre-target=0,代表从0开始刚好满足条件要求
        map.put(0,1);
        for(int i=0;i<nums.length;++i){
            pre+=nums[i];
            if(map.containsKey(pre-k)){
                count+=map.get(pre-k);
            }
            //将当前pre加入
            map.put(pre,map.getOrDefault(pre,0)+1);
        }
        return count;
    }
}
class NumArray {
    //本题使用前缀和算法,将使劲复杂度从O(n)降至O(1)
   private int[]preNums;

    public NumArray(int[] nums) {
        preNums=new int[nums.length+1];
        //循环时创建前缀和
        for(int i=1;i<preNums.length;++i){
            preNums[i]=preNums[i-1]+nums[i-1];
        }
        

    }
    
    public int sumRange(int left, int right) {
        return preNums[right+1]-preNums[left];
    }
}

2.和为 k 的子数组 力扣

题目描述

Java前缀和算法

解题思路

这道题就是对我们一维数组前缀和算法的扩展了,它不仅仅使用简单的前缀和数组进行存储数组的前缀和,他同样需要使用hash表进行元素的记录和存储,这是一道前缀和数组和哈希表进行融合的题目,具体的解题思路如下:我们对于这道题目而言,难点在于对于简单的前缀和数组而言,我们无法判断从哪个地方进行断开(无法判断从那个地方作为起点,哪个地方作为终点是符合条件的),因为我们在此基础上使用哈希表,哈希表中存储的是当前元素为基准的前缀和与target之间的差距,如果差距为0(恰好满足)或者当前差距等于其前面元素的前缀和(如果前面元素的前缀和为4,此时前缀和-target=4 :前缀和比我们需要的和的值大4,刚好与前面的值进行抵消),这种也是符合条件的。

图示如下:

Java前缀和算法

实例代码

我们给出实例代码

class Solution {
    public int subarraySum(int[] nums, int k) {
        //单纯使用前缀和,无法判别从哪里进行区分,这时我们需要将符合条件的数值直接存入map,再次需要时直接拿来使用即可
        int count=0;//记录最终的结果
        int pre=0;//记录前缀和
        //创建map
        HashMap<Integer,Integer>map=new HashMap<>();
        //如果是pre-target=0,代表从0开始刚好满足条件要求
        map.put(0,1);
        for(int i=0;i<nums.length;++i){
            pre+=nums[i];
            if(map.containsKey(pre-k)){
                count+=map.get(pre-k);
            }
            //将当前pre加入
            map.put(pre,map.getOrDefault(pre,0)+1);
        }
        return count;
    }
}

3.二维子矩阵的和力扣

题目描述

Java前缀和算法

解题思路

这道题正是我们上面讲述的关于二维数组求前缀和的典型例题,我们在上面模板的基础上进行思路的讲解:对于红框内的元素和,等于绿框元素-蓝框-红框+黑框(其中蓝框和红框中也有黑框的部分,但是被颜色覆盖了),在此之前,我们首先要将元素进行初始化,初始化的过程相当于对元素进行分割的逆过程:单位元素加上两边的元素再减去重复的元素

Java前缀和算法

Java前缀和算法

实例代码

class NumMatrix {
    //创建二维的数组和
  private int[][]preNums;

    public NumMatrix(int[][] matrix) {
        preNums=new int[matrix.length+1][matrix[0].length+1];
        //创建并遍历
        for(int i=1;i<preNums.length;++i){
            for(int j=1;j<preNums[0].length;++j){
                preNums[i][j]=preNums[i][j-1]+preNums[i-1][j]-preNums[i-1][j-1]+matrix[i-1][j-1];
            }
        }
    }
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
        return preNums[row2+1][col2+1]-preNums[row2+1][col1]-preNums[row1][col2+1]+preNums[row1][col1];
    }
}

4.除自身以外数组的乘积力扣

题目描述

Java前缀和算法

解题思路

这道题目的解题思路相对而言比较巧妙,它将前缀和拓展为前缀积,而除了本身元素之外的其他元素的和,则恰好符合前缀积中premul[i]*lastmul[i]的定义,所以直接定义前缀后缀积进行元素的相乘即可

实例代码

class Solution {
    public int[] productExceptSelf(int[] nums) {
        //解题思路:通过前缀积和后缀积进行相乘,最终的结果就是除了nums[i]之外的相乘结果,需要注意的是:需要对前后缀积的含义进行一定的改变
        //定义前缀积
        int[]pre=new int[nums.length];
        //定义后缀积
        int[]last=new int[nums.length];
        //初始化并相乘
        pre[0]=1;
        for(int i=1;i<nums.length;++i){
            pre[i]=nums[i-1]*pre[i-1];
        }
        last[nums.length-1]=1;
        for(int i=nums.length-2;i>=0;--i){
            last[i]=last[i+1]*nums[i+1];
        }
        int[]res=new int[nums.length];
        //进行最后的结果返回
        for(int i=0;i<nums.length;++i){
            res[i]=pre[i]*last[i];
        }
        return res;
    }
}

到了这里,关于Java前缀和算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 七大排序算法——希尔排序,通俗易懂的思路讲解与图解(完整Java代码)

    排序:所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 上述待排序的数中,有两个5。 将 前面 的5标记一个a, 将 后面 的5标记一个b。 通过算法进行排序后,这一组数就有序了, 但是要看两个相同的5的位置是否有改变。

    2024年02月03日
    浏览(52)
  • Qt学习笔记之二--创建一个简单的qt互动界面(超级无敌巨详细,0基础也能会,主打的就是图多,语句通俗)

      选择第一个选项,然后两个下一步------ 直到   这里要选择基类,我们选择Qwiget  至于为什么,可以看看我收藏的这篇博客QMainWindow和QWidget的区别_qwidget和qmainwindow_独行侠_阿涛的博客-CSDN博客 ok,创建完成后,我们使用快捷键Ctrl+R来运行一下,看看是否会弹出小窗口,弹出说

    2024年02月05日
    浏览(56)
  • 自动驾驶就是在扯?比亚迪你凭什么?

    比亚迪“炮轰”自动驾驶         上周,在比亚迪2022年财报交流会上,有投资人问比亚迪在自动驾驶方面的发展进度和规划,比亚迪集团董事长王传福直言:“无人驾驶那都是扯淡,弄个虚头巴脑的东西那都是忽悠,它就是一场皇帝的新装......自动驾驶一场车祸就让品牌

    2023年04月22日
    浏览(49)
  • 通俗易懂:什么是拉链表

    拉链表是数据仓库中一种重要的模型,相信很多数据工作者都接触过,面试也是经常考察的点。 但是很多人第一次接触“拉链表”这个词,难免会产生疑惑:拉链表是什么? 按照度娘的解释:“拉链表是一种针对数据仓库设计中表存储数据的方式而定义的数据模型,它有点

    2024年02月08日
    浏览(39)
  • 通俗介绍:什么是 Redis ?

    刚接触 Redis 的伙伴们可能会因为不熟悉而感到困惑。本文简述 Redis 是什么、有哪些作用的问题,是一篇短浅而入门级别的文章。 Redis官网:Redis 打开 Redis 官网可以看到,官方对 Redis 的介绍是这样的: The open source, in-memory data store used by millions of developers as a database , cache ,

    2024年02月08日
    浏览(38)
  • 通俗理解Jenkins是什么?

    目录 通俗理解 Jenkins是什么? 假设你有一个软件项目,多个开发者在一起写代码。每当有人提交新的代码时,你想要自动地构建、测试这些代码,确保它们没有引入问题。 Jenkins就像一个聪明的助手,会在有人提交新代码时自动检测,并告诉你是否一切正常。如果有问题,

    2024年02月05日
    浏览(28)
  • 通俗解释什么是NFT,NFT到底是什么

    一、快速了解 NFT,可以简单类比 房产证 ,把房子换成图片、视频、声音等各种数字资产,纸质证书换成去中心化的数字认证,就变成NFT了。 拥有一个NFT就代表拥有“对应某个数字资产所有权”的证书。 最早的NFT养猫游戏 CryPtoKitties 二、扩展知识 1. 详细解释 NFT全称Non-Fung

    2024年02月03日
    浏览(42)
  • 通俗讲解什么是Socket通讯

    Socket ,即套接字。就是两台主机之间逻辑连接的 端点 。(通俗来说:网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket)。 Socket是一套用于不同主机之间通信的API,它工作在我们的TCP/IP协议栈之上,可应用于浏览器、手机应用或用于

    2024年02月06日
    浏览(38)
  • 为什么LTD独立站就是Web3.0网站!

    Web3.0再次进入大众的视野,中国证监会科技监管局局长姚前发表文章《Web3.0 是渐行渐近的新一代互联网》,这篇文章中说到,Web1.0为“可读”(read),Web2.0 为“可读 + 可写”(read+write),Web3.0 则是“可读 +可写 + 拥有”(read+write+own)。 Web3.0数字经济时代破浪而来 初代互

    2024年01月23日
    浏览(72)
  • 通俗解释什么是(ip、网段、端口)

    IP地址被用来给Internet上的电脑一个编号。IP地址是一个32位的二进制数,通常被分割为4个“8位二进制数”(也就是4个字节),IP地址通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~255之间的十进制整数。IP即为身份证唯一。 举例:10.29.133.126。实际上是32位

    2024年02月15日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包