将数组分成和相等的三个部分

给你一个整数数组 A,只有可以将其划分为三个和相等的非空部分时才返回 true,否则返回 false。

形式上,如果可以找出索引 i+1 < j 且满足 (A[0] + A[1] + ... + A[i] == A[i+1] + A[i+2] + ... + A[j-1] == A[j] + A[j-1] + ... + A[A.length - 1]) 就可以将数组三等分。

示例 1:

输出:[0,2,1,-6,6,-7,9,1,2,0,1]
输出:true
解释:0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1
示例 2:

输入:[0,2,1,-6,6,7,9,-1,2,0,1]
输出:false
示例 3:

输入:[3,3,6,5,-2,2,5,1,-9,4]
输出:true
解释:3 + 3 = 6 = 5 - 2 + 2 + 5 + 1 - 9 + 4

解题思路:

方法一:寻找切分点
我们将数组 A 中的所有数的和记为 sum(A)。根据题目我们可以得知,每一个非空部分的和都应当是 sum(A) / 3。因此我们需要找到索引 i 和 j 使得:

(1)A[0] + A[1] + ... + A[i] = sum(A) / 3;

(2)A[i + 1] + A[i + 2] + ... + A[j] = sum(A) / 3。

(1)+(2)这等价于 A[0] + A[1] + ... + A[j] = sum(A) / 3 * 2 且 j > i。

首先我们需要找出索引 i。具体地,我们从第一个元素开始遍历数组 A 并对数组中的数进行累加。当累加的和等于 sum(A) / 3 时,我们就将当前的位置置为索引 i。由于数组中的数有正有负,我们可能会得到若干个索引 i0, i1, i2, ...,从 A[0] 到这些索引的数之和均为 sum(A) / 3。那么我们应该选取那个索引呢?直觉告诉我们,应该贪心地选择最小的那个索引 i0,这也是可以证明的:假设最终的答案中我们选取了某个不为 i0 的索引 ik 以及另一个索引 j,那么根据上面的两条要求,有:

A[0] + A[1] + ... + A[ik] = sum(A) / 3;

A[0] + A[1] + ... + A[j] = sum(A) / 3 * 2 且 j > ik。

然而 i0 也是满足第一条要求的一个索引,因为 A[0] + A[1] + ... + A[i0] = sum(A) / 3 并且 j > ik > i0,我们可以将 ik 替换为 i0,因此选择最小的那个索引是合理的。

在选择了 i0 作为 i 之后,我们从 i0 + 1 开始继续遍历数组 A 并进行累加,当累加的和等于 sum(A) / 3 * 2 时,我们就得到了索引 j,可以返回 true 作为答案。如果我们无法找到索引 i 或索引 j,或者 sum(A) 本身无法被 3 整数,那么我们返回 false。

 

int a = [0,2,1,-6,6,-7,9,1,2,0,1];

class Solution {
    public boolean canThreePartsEqualSum(int[] A) {
        //数组内元素的总和
        int arraySum = 0;

        for(int a : A){
            arraySum += a; 
        }

        //如果数组的总和和3 取余 != 0 那么直接返回false
        if(arraySum % 3 != 0){
            return false;
        }

        //算出数组的总和除以3的平均数
        int avgSum = arraySum/3;

        //从数组的0下标开始遍历 算出a[0]+a[1]+...+a[i-1]之间 相邻元素相加之和=avgSum 即算出了第一段的结束坐标
        int stepSum = 0;
        //第一段数组的求和结束后 标记+1 第二段数组的求和结束后 标记+1 因为初始的时候 我们就已经对数组平分了3段处理,所以
        //第3段 肯定符合 最终标记字段总数=2 即数组符合标准
        int flgCount = 0;
        for(int i =0;i<A.length;i++){
            stepSum += A[i];
            if(flgCount == 2){
                return true;
            }
            if(stepSum == avgSum){
                stepSum = 0;
                flgCount ++;
            }
        }

        //默认返回false
        return false;
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值