一、题目理解
- 核心目标:给定一个表示红包金额的数组
redpacks,通过做两次切割将其分成三部分,要使得第一部分和第三部分的红包总金额相等,最终求出第一部分所能达到的最大总金额。 - 题目理解:即将数组
redpacks分成三部分,前后两部分要相等并且要最大的值,很容易理解到这个题目涉及前后双指针操作。
二、解题思路分析
-
暴力解法思路 :
- 通过两层循环来模拟两次切割的位置。外层循环控制第一次切割的位置,内层循环控制第二次切割的位置。
-
双指针思路 :
- 用两个指针
left和right,初始分别指向数组的开头和结尾。 - 计算左边部分的和
left_sum以及右边部分的和right_sum,移动指针来调整两部分的和,使其尽量相等。 - 如果
left_sum小于right_sum,就将left指针向右移动一位(意味着把下一个红包加到左边部分),同时更新left_sum;反之,如果left_sum大于right_sum,就将right指针向左移动一位,并更新right_sum。 - 在移动指针的过程中,当
left_sum和right_sum相等时,记录下此时左边部分的和,并与之前记录的最大相等金额比较,更新最大值。 - 代码示例(Python 语言伪代码形式,体现思路) :
- 用两个指针
public static int solution(List<Integer> redpacks) {
// Please write your code here
int n = redpacks.size();
if (n < 2)
return 0; // 如果红包数量小于2,无法分成三部分
int maxBonus = 0;
int leftSum = redpacks.get(0);
int rightSum = redpacks.get(n - 1);
// 使用双指针遍历红包列表
int left = 0;
int right = n - 1;
while (left < right) {
if (leftSum == rightSum) {
maxBonus = Math.max(leftSum, maxBonus);
left++;
right--;
leftSum += redpacks.get(left);
rightSum += redpacks.get(right);
} else if (leftSum < rightSum) {
left++;
leftSum += redpacks.get(left);
} else if (leftSum > rightSum) {
right--;
rightSum += redpacks.get(right);
}
}
return maxBonus;
}
-
复杂度分析:
- 时间复杂度:指针最多遍历整个数组一次,时间复杂度为O(n),相比暴力解法有很大提升,
n为红包数组长度。 - 空间复杂度:同样主要是几个指针和临时变量的使用,空间复杂度为O(1)。
- 时间复杂度:指针最多遍历整个数组一次,时间复杂度为O(n),相比暴力解法有很大提升,
三、代码解读
-
通过
while循环,只要左指针left小于右指针right,就持续进行循环操作,不断调整指针位置以及对应的左右部分红包总金额,来寻找满足条件的情况。 -
情况一:左右部分金额相等:
- 当
leftSum等于rightSum时,说明找到了一种满足第一部分和第三部分红包总金额相等的情况。此时,通过Math.max函数将当前的leftSum和已记录的maxBonus进行比较,取较大值更新maxBonus,因为我们要找的是最大的满足条件的金额。 - 然后将
left指针向右移动一位(left++),right指针向左移动一位(right--),同时更新leftSum和rightSum,把新指针指向的红包金额分别累加到对应的总和中,继续下一轮判断。
- 当
-
情况二:左边部分金额小于右边部分金额:
- 当
leftSum < rightSum时,意味着左边部分的红包总金额较小,需要将左指针向右移动,把下一个红包纳入左边部分,所以执行left++,并且将新纳入的红包金额累加到leftSum中(leftSum += redpacks.get(left)),之后继续下一轮循环判断,期望通过这样的调整使左右两边金额逐渐接近相等。
- 当
-
情况三:左边部分金额大于右边部分金额:
-
当
leftSum > rightSum时,和情况二类似,不过此时是右边部分的红包总金额较小,需要将右指针向左移动,把新纳入的红包(也就是当前右指针新指向的红包)加到右边部分,执行right--以及rightSum += redpacks.get(right),然后继续下一轮循环判断。
-