题目:一个整型(非负)数组,将其划分为总和相同的4个切片,例如:{2,5,1,1,1,1,4,1,7,3,7},切片操作后划分为:{2,5},{1,1,1,4},{7},{7},也就找到所谓的四等分点,切分点分别是1,1,3,切分点不算在总和内。输出能否分片。要求时间复杂度和空间复杂度为o(n)。
思路:
- 两个Map,一个存<总和,位置>,一个存<位置,总和>。
- 由于数组非负,先由头尾指针向中间逼近,先将数组分成三段< L,M,R >,如果左边大则左移尾指针,如果右边大则右移头指针,最后使得左右部分相等,大小记为A。
- 然后处理中间部分M,看其能否分成两部分< LM,LR >,使得他们的总和都等于A。方法是从<总和,位置>的Map中找总和为2A的位置i,即满足LM==L,如果这个位置i存在并且在左右指针之间,根据尾指针和i以及Map<位置,总和>算出LR的和,判断是否等于A,如果相等,则输出true
{2,5,1,1,1,1,4,1,7,3,7}
↑ ↑ ↑
头指针 i 尾指针
以下代码通过了在线编程的所有测试用例:
public class FourPiece {
public static void main(String[] args){
// int[] nums = {2,5,4,7,2,5,4,7,2,5,4,7,2,5,4,7,2,5,4,7,2,5,4,7};
// int[] nums = {1,2,1,3,1,4,2};
int[] nums = {2,5,1,1,1,1,4,1,7,3,7};
// int[] nums = {10,2,11,13,1,1,5,1,1,1,10,2,11,12,5,1,1,1,1,1,10,2,11,10,5,1,1,1,1,34};
System.out.print(canDevide(nums));
}
public static boolean canDevide(int[] nums){
if(nums == null || nums.length < 7){
return false;
}
HashMap<Long,Integer> indexMap = new HashMap<Long,Integer>();//<总和,位置>
HashMap<Integer,Long> sumMap = new HashMap<Integer,Long>();//<位置,总和>
long curSum = 0;
for(int i = 0; i < nums.length; i++){
indexMap.put(curSum,i);
sumMap.put(i,curSum);
curSum += nums[i];
}
long leftSum = nums[0];//最左段总和
long rightSum = nums[nums.length - 1];//最右段总和
int leftIndex = 1;//左分割点
int rightIndex = nums.length - 2;//右分割点
while(leftIndex + 3 < rightIndex){
if(leftSum == rightSum){
if(indexMap.get((leftSum << 1) + nums[leftIndex]) != null){
int middleIndex = indexMap.get((leftSum << 1) + nums[leftIndex]);//中间分割点
if(middleIndex > leftIndex + 1 && middleIndex < rightIndex - 1){
if(sumMap.get(rightIndex) - sumMap.get(middleIndex + 1) == leftSum){
return true;
}
}
}
leftSum += nums[leftIndex++];
rightSum += nums[rightIndex--];
}
else{
if(leftSum < rightSum){
leftSum += nums[leftIndex++];
}
else{
rightSum += nums[rightIndex--];
}
}
}
return false;
}
}