1. 题目背景与问题描述
今天我们来拆解LeetCode第2529题"正整数和负整数的最大计数"。这是一道典型的数组统计问题,主要考察对数组元素的遍历和条件判断能力。题目要求我们统计一个已排序数组中正整数和负整数的数量,并返回两者中的较大值。
给定一个非递减顺序排列的整数数组nums,我们需要:
- 统计数组中负整数的数量neg
- 统计数组中正整数的数量pos
- 返回max(neg, pos)
注意:0不被视为正数也不被视为负数,所以遇到0时不做计数。
2. 解题思路分析
2.1 暴力解法
最直观的解法是直接遍历整个数组,使用两个计数器分别记录正负数的数量:
def maximumCount(nums): pos = neg = 0 for num in nums: if num > 0: pos += 1 elif num < 0: neg += 1 return max(pos, neg)时间复杂度:O(n),需要完整遍历数组 空间复杂度:O(1),只使用了常数空间
2.2 利用有序特性优化
由于数组是非递减排序的,我们可以利用这个特性进行优化:
- 使用二分查找找到第一个非负数的位置,这个位置之前的都是负数
- 使用二分查找找到第一个正数的位置,这个位置之后的都是正数
- 计算neg和pos的数量
import bisect def maximumCount(nums): neg = bisect.bisect_left(nums, 0) pos = len(nums) - bisect.bisect_right(nums, 0) return max(neg, pos)时间复杂度:O(log n),使用了两次二分查找 空间复杂度:O(1)
3. 代码实现详解
3.1 二分查找实现细节
让我们详细看看二分查找的实现:
def find_first_non_negative(nums): left, right = 0, len(nums) while left < right: mid = (left + right) // 2 if nums[mid] < 0: left = mid + 1 else: right = mid return left def find_first_positive(nums): left, right = 0, len(nums) while left < right: mid = (left + right) // 2 if nums[mid] <= 0: left = mid + 1 else: right = mid return left3.2 边界条件处理
需要考虑的特殊情况:
- 数组全为正数
- 数组全为负数
- 数组包含0
- 空数组
4. 复杂度分析与比较
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 暴力解法 | O(n) | O(1) | 简单直接,适合小规模数据 |
| 二分查找 | O(log n) | O(1) | 大规模数据,性能更优 |
5. 测试用例设计
好的测试用例应该覆盖各种边界情况:
test_cases = [ ([-2,-1,-1,1,2,3], 3), # 正常情况 ([-3,-2,-1,0,0,1,2], 3), # 包含0 ([5,20,66,1314], 4), # 全正数 ([-1,-1,-1], 3), # 全负数 ([0,0,0], 0), # 全0 ([], 0), # 空数组 ]6. 常见错误与调试技巧
6.1 常见错误
- 忘记处理0的情况
- 二分查找的边界条件处理不当
- 正负数计数时逻辑错误
6.2 调试技巧
- 打印中间变量检查二分查找的分界点
- 使用小规模测试数据手动验证
- 检查数组全正/全负的特殊情况
7. 实际应用场景
这类计数问题在实际开发中很常见,比如:
- 用户评价统计(好评/差评数量)
- 日志分析(成功/失败请求计数)
- 金融交易记录(收入/支出统计)
8. 性能优化进阶
对于特别大的数组,还可以考虑:
- 并行计算:将数组分块,多线程统计
- SIMD指令优化:使用向量化指令加速遍历
- 预处理:如果数组不变,可以预先计算并缓存结果
9. 类似题目推荐
- 统计有序矩阵中的负数(LeetCode 1351)
- 在排序数组中查找元素的第一个和最后一个位置(LeetCode 34)
- 山脉数组的峰顶索引(LeetCode 852)
10. 个人解题心得
在实际解决这个问题时,我最初直接使用了暴力解法,因为代码简单不易出错。后来考虑到题目给出的数组是有序的这个重要条件,才想到可以使用二分查找优化。这提醒我在解题时一定要仔细阅读题目给出的所有条件,特别是那些看似"显而易见"的条件,往往就是优化的关键。
另一个收获是关于二分查找边界条件的处理。在实现find_first_non_negative和find_first_positive时,我最初混淆了两种情况的判断条件,导致测试失败。通过这个小错误,我更加理解了二分查找中"寻找左边界"和"寻找右边界"的区别。