LeetCode724 560 437 1248前缀和

前缀和

  1. LeetCode724. 寻找数组的中心下标
    724. 寻找数组的中心下标
    给你一个整数数组 nums ,请计算数组的 中心下标 。
    数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
    如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

两次遍历,分别求第i个数的前缀和以及后缀和,再判断是否存在一个数的前缀和等于后缀和。

	public static int pivotIndex(int[] nums) {
        //前缀
        int[] pre = new int[nums.length];
        pre[0] = 0;
        for (int i = 1; i < nums.length; i++) {
            pre[i] = pre[i - 1] + nums[i - 1];
        }

        //后缀
        int[] suf = new int[nums.length];
        suf[nums.length - 1] = 0;
        for (int i = nums.length - 2; i >= 0; i--) {
            suf[i] = suf[i + 1] + nums[i + 1];
        }

        for (int i = 0; i < nums.length; i++) {
            if(pre[i] == suf[i]){
                return i;
            }
        }
        return -1;
	}

优化:存在数学规律:数组总和为total,当一个数的左右和sum相等时,存在关系式:2*sum+num[i] == total
并且中心索引左侧或右侧没有元素时,即为零个项相加,也成立。

	public static int pivotIndex(int[] nums) {
        //数学规律:总和为total,左右和sum相等时,存在2*sum+num[i] == total
        //中心索引左侧或右侧没有元素时,即为零个项相加,也成立
        int total = Arrays.stream(nums).sum();
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            if(2 * sum + nums[i] == total){
                return i;
            }
            sum += nums[i];
	}
  1. LeetCode560. 和为 K 的子数组
    560. 和为 K 的子数组
    给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的连续子数组的个数 。
    子数组是数组中元素的连续非空序列。

示例1:
输入:nums = [1,1,1], k = 2
输出:2

示例 2:
输入:nums = [1,2,3], k = 3
输出:2

双指针遍历子数组,找到和为k的子数组的个数

        int count = 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){
                    count++;
                }
            }
        }

使用前缀和,找到所有i ,j前缀和的差等于k的组合个数

        int[] sum = new int[nums.length + 1];
        sum[0] = 0;
        for (int i = 0; i < nums.length; i++) {
            sum[i + 1] = sum[i] + nums[i];
        }
        int count = 0;
        for (int i = 0; i < nums.length + 1; i++) {
            for (int j = i + 1; j < nums.length + 1; j++) {
                if(sum[j] - sum[i] == k){
                    count++;
                }
            }
        }

优化,不需要把前缀和计算出来之后再两次遍历。使用map存储前缀和的种类以及个数。

HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);//-1处的前缀和是0
        int sum = 0;
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            int key = sum - k;
            if(map.containsKey(key)){
                count += map.get(key);
            }
            if(map.containsKey(sum)){
                map.put(sum, map.get(sum) + 1);
            }else{
                map.put(sum, 1);
            }
        }
        return count;
  1. LeetCode437 路径总和 III
    437. 路径总和 III
    给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
    路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。

=
在这里插入图片描述
使用前缀和+map
从根节点开始计算前缀和,需要把初始处:key=0 value=1 先写入。
注意sum会溢出的问题!!!

class Solution {
    //前缀和+map
    //注意sum不能溢出
    HashMap<Long, Integer> map = new HashMap<>();
    int count = 0;
    public int pathSum(TreeNode root, int targetSum) {
        map.put(0L, 1);
        dfs(root, 0L, targetSum);
        return count;
    }

    public void dfs(TreeNode root, Long sum, int targetSum) {
        if(root == null){
            return;
        }
        sum += root.val;
        if(map.containsKey(sum - targetSum) && map.get(sum - targetSum) >= 1){
            count += map.get(sum - targetSum);
        }
        map.put(sum, map.getOrDefault(sum, 0) + 1);
        dfs(root.left, sum, targetSum);
        dfs(root.right, sum, targetSum);
        map.put(sum, map.get(sum) - 1);
    }
}
  1. LeetCode1248. 统计「优美子数组」
    统计「优美子数组」
    给你一个整数数组 nums 和一个整数 k。如果某个连续子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。
    请返回这个数组中 「优美子数组」 的数目。

示例 1:
输入:nums = [1,1,2,1,1], k = 3
输出:2
解释:包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1] 。
示例 2:
输入:nums = [2,4,6], k = 1
输出:0
解释:数列中不包含任何奇数,所以不存在优美子数组。
示例 3:
输入:nums = [2,2,2,1,2,2,1,2,2,2], k = 2
输出:16

计算前缀含有的素数的个数+map

public int numberOfSubarrays(int[] nums, int k) {
        //前缀素数和+map
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);//-1出的素数个数为0
        int count = 0;
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            if(nums[i] % 2 == 1){
                count++;
            }
            if(map.containsKey(count - k)){
                res += map.get(count - k);
            }
            map.put(count, map.getOrDefault(count, 0) + 1);
        }
        return res;
    }
  1. 美团0826 平均数为k的最长连续子数组
    平均数为k的最长连续子数组
    前缀和+map
    给定n个正整数组成的数组,求平均数正好等于k 的最长连续子数组的长度。

输入描述:
第一行输入两个正整数n和k,用空格隔开。
第二行输入n个正整数ai,用来表示数组。
范围:1<=n<=200000 1<=k,ai<=10^9

输出描述:
如果不存在任何一个连续子数组的平均数等于k,则输出-1。
否则输出平均数正好等于k的最长连续子数组的长度。

示例
输入:
5 2
1 3 2 4 1
输出 3

思路:平均数比较难处理,我们不妨将原数组中每个元素都-k,这样问题转换成找到和为0的最长子数组。
求每个元素的前缀和,如果map中存在和当前元素的前缀和相等,就表示这两个前缀和之前的元素的和是0。
为了求出最长的子数组的长度,map key保存前缀和,value保存key出现的最左边的下标。

public static void main(String[] args) {
        //所有元素都-k 找和为0的最长的子数组的长度
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int k = scanner.nextInt();
        int[] nums = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = scanner.nextInt() - k;
        }
        HashMap<Long, Integer> map = new HashMap<>();
        int maxLen = 0;
        long sum = 0;
        map.put(0L, -1);
        for (int i = 0; i < n; i++) {
            sum += nums[i];
            if(map.containsKey(sum)){
                maxLen = Math.max(maxLen, i - map.get(sum));
            }else{
                map.put(sum, i);
            }
        }
        if(maxLen == 0){
            System.out.println("-1");
            return;
        }
        System.out.println(maxLen);

    }
  1. 美团0909第二题
    小美有一个数组 a,对这个数组的元素求和:a1+a2+a3+…+an。现在,她想把其中一个加号变成减号,但小美是小学生,不会负数的加减法,因此计算过程中不能出现负数。
    小美想知道政变符号后含三的最小值是多少,如果个能改变行号,则输出-1。

输入描述:
第一行输入一个整数n(1<n<10^5)表示数组长度
第二行输入n个整数表示数组a(1<=ai<=10^9)。
输出描述:
输出改变符号后的答案,若无法攻变,则输出 -1
样例:
输入:
3
3 2 1
输出2
:3 - 2 = 1 + 1 = 2
:3 + 2 = 5 - 1 = 4

public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] nums = new int[n];
        long sum = 0;//元素的总和
        for (int i = 0; i < n; i++) {
            nums[i] = scanner.nextInt();
            sum += nums[i];
        }
        long preSum = 0;//前缀和
        long res = Integer.MAX_VALUE;
        for (int i = 0; i < n - 1; i++) {
            preSum += nums[i];
            if(preSum < nums[i + 1]){//当前数据的之前数据的和(前缀和)是否大于当前元素
                continue;//< 则当前元素不能改成-
            }
            //> 当前元素可以改成-
            //判断改减后和是否是正的
            if(sum - 2*nums[i + 1] >= 0){
                res = Math.min(res, sum - 2*nums[i + 1]);
            }
        }
        if(res == Integer.MAX_VALUE){
            System.out.println("-1");
        }else {
            System.out.println(res);
        }

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值