题目概述(简单难度)
给你一个整数数组 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]) 就可以将数组三等分。
附上leetcode链接:
点击此处进入leetcode
示例 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
思路与代码
思路展现
这个题目非常的经典,思路是这样的:
1:如果想要把一个数组分成三个部分,且这三个部分每个部分数字加起来的和还要相等,就意味着我们数组中所有数字加起来的和是必须可以被3取余的,如果不能被3取余,就意味着当取平均数target的时候是一个小数.
2:定义一个临时变量cur去记录我们每个部分的和,然后我们开始从头遍历我们的数组,当遍历到某个数组下标时,此时的cur的值等于我们的平均数target的时候,停止我们的遍历,此时代表我们第一部分已经划分完毕了,假设遍历到数组最后一个元素,我们变量cur的值还不等于target,就说明这个数组中不存在三个和相等的非空部分,直接返回false即可
3:欧克那么我们现在第一部分已经找到了,但这还远远不够,为什么呢?
原因是第二部分的和未必是等于target的,并且还有一种情况,就是即使第二部分和等于target,我们的第三部分也未必存在,所以还需要进行第三部分是否存在的判断,所以首先我们先从第一部分的最后一个元素的下一个元素作为我们第二部分遍历开始的地点,所以我们定义j=i+1,但是要注意这里的循环条件我们有一个小技巧在这里:首先我们需要明确的一点就是假设前两部分的和都等于target,且第三部分还存在,那么我们根本就不用判断第三部分的和是否等于target,因为它一定等于target.所以此处的循环条件我们可以设置为j+1
然后到了我们循环体的内部:我们退出循环的条件是当cur的值等于二倍的target的值的时候,return true,说明此时我们数组找到了第二部分,且第三部分存在.
代码示例
class Solution { public boolean canThreePartsEqualSum(int[] A) { int s = 0; //s代表所有数字的和 for (int num : A) { s += num; } //取余3不等于0说明根本划分不成桑格和相等的非空部分. if (s % 3 != 0) { return false; } int target = s / 3; //cur代表当前数字的和 int n = A.length, i = 0, cur = 0; while (i < n) { cur += A[i]; if (cur == target) { break; } ++i; } //这块是第一块非空部分都没出现的情况,那么就直接return false即可 if (cur != target) { return false; } //假设此时第一部分已经等于target,此时让下标指向第二部分的第一个元素 int j = i + 1; // 需要满足最后一个数组非空,所以循环条件为j+1<n while (j + 1 < n) { cur += A[j]; //当数组元素加到第二部分最后一个元素的时候就进行判断 if (cur == target * 2) { //如果前两部分所有的元素和等于两倍的target,返回true return true; } ++j; } return false; } }
总结
对于数组的巧用,需要大家牢记:
时间复杂度:O(N),其中 N是数组 A 的长度。我们最多只需要遍历一遍数组就可以得到答案。
空间复杂度:O(1)。我们只需要使用额外的索引变量 i,j 以及一些存储数组信息的变量。