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

127 阅读2分钟

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

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

示例 1:

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

示例 2:

输入: arr = [0,2,1,-6,6,7,9,-1,2,0,1]
输出: false

示例 3:

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

题解:

/**
 * @param {number[]} arr
 * @return {boolean}
 */
// 方法一:遍历计数
var canThreePartsEqualSum = function (arr) {
    // 求和
    let sum = arr.reduce((pre, cur) => pre + cur)
    // 如果余数不等于0,说明没法分成3等份
    if (sum % 3 !== 0) {
        return false;
    }
    let cnt = 0 // 计数
    let tem = 0 // 累加
    for (let i = 0; i < arr.length; i++) {
        tem += arr[i]
        if (tem == sum / 3) {
            cnt++
            tem = 0
        }
    }
    // sum不为0且 cnt == 3 时 return true
    // sum为0且 cnt > 3 时 return true(🌰[0, 0, 0, 0])
    return cnt >= 3
};
// 方法二:贪心
var canThreePartsEqualSum = function (arr) {
    // 计算出数组arr的和
    const sumArr = arr.reduce((per, cur) => per + cur);

    // 如果余数不等于0,说明没法分成3等份
    if (sumArr % 3 !== 0) {
        return false;
    }

    // 目标值,即每一等份需要凑成的值
    const Target = sumArr / 3;
    // 当前累加的和
    let currentSum = 0;
    // 第一个和为Target的数组的索引终止位置
    let i = 0;

    // 递归查找最小的终止索引i
    while (i < arr.length) {
        // 累计计算截至到当前索引i的数组和
        currentSum += arr[i];

        // 当当前数组和等于目标值时
        if (currentSum === Target) {
            // 退出循环
            break;
        }
        // 否则则继续进行累加
        i++;
    }

    /*
    关键点1
    这一步的判断,其实本质上是比较 数组arr的和 与 其值的三分之一
    因为在关键点1中,
    如果是通过break语句退出循环的,则二者必然相等,不可能执行本处if代码块里的内容。
    反之,如果不是通过break语句退出的,
    则意味着整个数组arr的项值都被累加进入变量currentSum中
    */
    if (currentSum !== Target) {
        /*
        返回false的涵义:
        请牢记,currentSum是数组arr的总和(在if代码块内时),
        Target是数组A总和的三分之一
        举例:
        数组arr的和为9,9可以被分成三等份,但是不意味着,数组arr就可以被等分成3个值为3的子数组,
        例如: 当数组A为[1,4,4]时,总和为9,能被3整除,可分成3份后每份值都不等([1], [4], [4])
        */
        return false;
    }
    /*
    第二个数值的终止索引,
    由于从索引0到索引i的值以满足Target,
    则第二个数组的起始值该为i+1
    */
    let j = i + 1;

    /*
    关键点2
    while循环最多只到j + 1 < arr.length为止
    因为至少需要留一个项数为1的数组来满足等份三份的需求
    */
    while (j + 1 < arr.length) {
        currentSum += arr[j];
        if (currentSum === Target * 2) {
            /*
            关键点3
            如果进入了这个if代码块内,
            则证明已经有2个数组的和等同于Target了
            又因为预先保证了至少预留一位的数组(while( j + 1 < arr.length))
            所以一共有3个数组的满足了Target了,
            所以可以直接返回true
            */
            return true;
        }
        j++;
    }

    /*
    这里的返回false是因为满足了第一个while循环,没有满足第二个while循环
    即只有一个数组的和等于Target
    */
    return false;
};

来源:力扣(LeetCode)

链接:leetcode.cn/problems/pa…

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。