☰
LeetCode 888公平糖果交换算法解析与优化
2026/11/4 5:52:06 网站建设 项目流程

1. 问题解析:公平糖果交换的核心逻辑

LeetCode 888题"公平糖果交换"是一个典型的数组操作问题,题目描述如下:Alice和Bob各自拥有不同数量的糖果盒,每个盒子里装有不同数量的糖果。他们希望通过交换一盒糖果,使得两人最终拥有的糖果总量相等。

这个问题的关键在于理解交换前后糖果总量的数学关系。设Alice的总糖果数为sumA,Bob的总糖果数为sumB。假设Alice给Bob的糖果数为a,Bob给Alice的糖果数为b,那么交换后应该满足:

sumA - a + b = sumB - b + a

这个等式可以简化为:a - b = (sumA - sumB)/2

这个简化后的等式就是我们解决问题的核心数学依据。它告诉我们,我们需要在Alice和Bob的糖果盒中找到两个数a和b,使得它们的差等于(sumA - sumB)/2。

2. 基础解法:排序+双重循环

2.1 解法思路分析

最直观的解法是对两个数组进行排序,然后使用双重循环来寻找符合条件的a和b。这种方法的步骤如下:

  1. 计算Alice和Bob各自的总糖果数sumA和sumB
  2. 对两个数组进行排序
  3. 根据sumA和sumB的大小关系决定遍历方向
  4. 使用双重循环寻找满足条件的a和b

这种解法的时间复杂度为O(nlogn + mlogm + nm),其中n和m分别是两个数组的长度。虽然时间复杂度较高,但思路清晰,容易理解和实现。

2.2 代码实现详解

class Solution { public: vector<int> fairCandySwap(vector<int>& aliceSizes, vector<int>& bobSizes) { sort(aliceSizes.begin(), aliceSizes.end()); sort(bobSizes.begin(), bobSizes.end()); int sa = 0, sb = 0, a, b; for(int& i : aliceSizes) sa += i; for(int& i : bobSizes) sb += i; if(sa < sb) { for(int i = 0; i < aliceSizes.size(); i++) { a = aliceSizes[i]; for(int j = bobSizes.size()-1; j >=0 ; j--) { b = bobSizes[j]; if(a >= b) break; if(sa - a + b == sb - b + a) { return {a, b}; } } } } else { for(int j = 0; j < bobSizes.size(); j++) { b = bobSizes[j]; for(int i = aliceSizes.size()-1; i >=0 ; i--) { a = aliceSizes[i]; if(a <= b) break; if(sa - a + b == sb - b + a) { return {a, b}; } } } } return {}; } };

2.3 优化技巧与注意事项

  1. 提前终止循环:在内层循环中,一旦发现当前a和b不可能满足条件,可以立即终止循环,减少不必要的计算。

  2. 遍历方向选择:根据sumA和sumB的大小关系决定遍历方向,可以更快找到解。例如,当sumA < sumB时,Alice应该给Bob较小的糖果,Bob给Alice较大的糖果。

  3. 边界条件处理:需要考虑空数组或无法找到解的情况,此时应返回空数组。

注意:这种解法在数据量较大时性能较差,适合作为理解问题的起点,但在实际面试或竞赛中应考虑更优解法。

3. 优化解法:单次遍历+数学推导

3.1 解法思路分析

基于前面的数学推导,我们知道需要找到a和b满足a - b = (sumA - sumB)/2。这可以转化为b = a - (sumA - sumB)/2。

因此,我们可以:

  1. 计算sumA和sumB
  2. 计算目标差值delta = (sumA - sumB)/2
  3. 将Alice的糖果放入哈希集合以便快速查找
  4. 遍历Bob的糖果,对于每个b,检查a = b + delta是否在Alice的集合中

这种方法的时间复杂度为O(n + m),空间复杂度为O(n)或O(m),取决于选择将哪个数组存入哈希集合。

3.2 代码实现详解

class Solution { public: vector<int> fairCandySwap(vector<int>& aliceSizes, vector<int>& bobSizes) { int suma = 0, sumb = 0; unordered_set<int> aliceSet; for(int a : aliceSizes) { suma += a; aliceSet.insert(a); } for(int b : bobSizes) { sumb += b; } int delta = (suma - sumb) / 2; for(int b : bobSizes) { int a = b + delta; if(aliceSet.count(a)) { return {a, b}; } } return {}; } };

3.3 性能分析与比较

  1. 时间复杂度:哈希集合解法为O(n + m),远优于双重循环的O(nlogn + mlogm + nm)

  2. 空间复杂度:哈希集合解法需要额外O(n)或O(m)的空间,而双重循环解法只需要O(1)额外空间

  3. 实际应用:在数据量较大时,哈希集合解法优势明显;数据量很小时,双重循环可能更快

4. 双指针优化解法

4.1 解法思路分析

另一种优化方法是使用双指针技巧,前提是数组已经排序。步骤如下:

  1. 计算sumA和sumB
  2. 计算目标差值delta = (sumA - sumB)/2
  3. 对两个数组排序
  4. 使用双指针同时遍历两个数组,寻找满足a - b = delta的元素对

这种方法的时间复杂度为O(nlogn + mlogm + n + m),空间复杂度为O(1)(不考虑排序的空间开销)。

4.2 代码实现详解

class Solution { public: vector<int> fairCandySwap(vector<int>& aliceSizes, vector<int>& bobSizes) { int suma = 0, sumb = 0; for(int a : aliceSizes) suma += a; for(int b : bobSizes) sumb += b; int delta = (suma - sumb) / 2; sort(aliceSizes.begin(), aliceSizes.end()); sort(bobSizes.begin(), bobSizes.end()); int i = 0, j = 0; while(i < aliceSizes.size() && j < bobSizes.size()) { int diff = aliceSizes[i] - bobSizes[j]; if(diff == delta) { return {aliceSizes[i], bobSizes[j]}; } else if(diff < delta) { i++; } else { j++; } } return {}; } };

4.3 适用场景与选择建议

  1. 数据量中等:当数据量不是特别大时,双指针解法是一个很好的平衡选择

  2. 内存受限:如果内存资源紧张,无法使用哈希集合,双指针解法是更好的选择

  3. 已排序数据:如果输入数据已经排序,双指针解法的时间复杂度可以降低到O(n + m)

5. 常见问题与调试技巧

5.1 边界条件处理

  1. 总和差为奇数:如果(sumA - sumB)是奇数,直接返回空,因为无法通过交换整数数量的糖果达到平衡

  2. 空输入:处理一个或两个数组为空的情况

  3. 无解情况:虽然题目保证有解,但在实际应用中需要考虑无解时的处理

5.2 调试技巧

  1. 打印中间结果:在计算sumA、sumB和delta时打印这些值,确保计算正确

  2. 小规模测试:使用小规模数据手动计算预期结果,验证代码正确性

  3. 性能分析:对于大规模数据,测量不同解法的时间,验证时间复杂度分析

5.3 常见错误

  1. 整数溢出:当糖果数量很大时,sumA和sumB可能溢出,可以使用long long类型

  2. 错误的差值计算:确保delta的计算是(sumA - sumB)/2而不是(sumB - sumA)/2

  3. 排序遗漏:双指针解法必须确保数组已排序

6. 算法扩展与变种思考

6.1 多盒交换问题

如果允许交换多盒糖果,问题会变得更加复杂。这种情况下可以考虑:

  1. 背包问题变形:将问题转化为寻找两个子集,使得它们的和满足特定关系

  2. 动态规划解法:使用DP来记录可能的和组合

6.2 最小交换代价问题

如果每次交换都有代价(如糖果数量的差值),可以寻找代价最小的交换方案:

  1. 优先队列:维护可能的交换对,按代价排序

  2. 贪心算法:每次选择当前最优的交换对

6.3 多维扩展

如果糖果有多个属性(如大小和口味),需要同时满足多个条件的平衡,问题会变得更加复杂:

  1. 多目标优化:可能需要使用线性规划或其他优化技术

  2. 近似算法:在多项式时间内寻找近似解

在实际编程面试中,理解基础解法并能逐步优化是非常重要的。这道题目很好地展示了如何从暴力解法出发,通过数学分析和数据结构选择,逐步优化算法性能的过程。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询