第一次在力扣上遇到"消失的数字"这个问题时,我下意识觉得这不过是个简单的数组遍历练习。但当我深入思考后,才发现其中蕴含着计算机科学中多个经典思想的实际应用场景。题目描述很简单:给定一个包含n个不同整数的数组nums,其中数字范围在[0, n]内,找出[0, n]范围内没有出现在数组中的那个数字。
这个看似简单的问题实际上考察了程序员对数据结构、算法效率以及数学思维的掌握程度。在面试场景中,面试官往往会要求候选人给出多种解法并分析各自的时间/空间复杂度。下面我将分享四种不同思维路径的解决方案,从最直观的暴力解法到巧妙的数学技巧,每种方法都体现了不同的算法设计思想。
哈希表法是解决查找类问题的经典方案。具体思路是:首先创建一个哈希集合存储所有数组元素,然后遍历0到n的数字,检查哪个数字不在集合中。
python复制def missingNumber(nums):
num_set = set(nums)
n = len(nums)
for number in range(n + 1):
if number not in num_set:
return number
提示:虽然这种方法不是最优解,但在实际工程中,当内存不是瓶颈时,这种清晰易懂的方案往往是首选。
在真实场景中,我们可以利用语言特性进一步优化。比如在Python中,集合的in操作平均时间复杂度是O(1),但最坏情况下可能退化到O(n)。对于确定性要求高的场景,可以考虑使用位数组代替哈希集合:
python复制def missingNumber(nums):
n = len(nums)
present = [False] * (n + 1)
for num in nums:
present[num] = True
for i in range(n + 1):
if not present[i]:
return i
这是最优雅的解决方案之一,利用了数学中著名的求和公式。我们知道0到n的和可以用高斯公式计算:(n*(n+1))/2。用这个总和减去数组中所有数字的和,差值就是缺失的数字。
python复制def missingNumber(nums):
n = len(nums)
expected_sum = n * (n + 1) // 2
actual_sum = sum(nums)
return expected_sum - actual_sum
虽然这个方案简洁,但需要注意几个边界情况:
异或运算有几个重要性质:
我们可以利用这些性质:将0到n的所有数字与数组中的所有数字进行异或,最终结果就是缺失的数字。
python复制def missingNumber(nums):
missing = len(nums)
for i, num in enumerate(nums):
missing ^= i ^ num
return missing
假设nums = [3,0,1],n=3:
位运算方案在嵌入式开发等资源受限环境中特别有价值,因为它:
先对数组进行排序,然后使用二分查找来定位缺失的数字。具体来说,我们可以检查中间元素的值是否等于其索引,根据比较结果决定向左还是向右搜索。
python复制def missingNumber(nums):
nums.sort()
left, right = 0, len(nums)
while left < right:
mid = (left + right) // 2
if nums[mid] > mid:
right = mid
else:
left = mid + 1
return left
注意:虽然这种方法时间复杂度不是最优,但在某些特定场景下(比如数组已经部分有序),可能会有意外的高效表现。
这种方案适合以下情况:
| 方案 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 哈希表法 | O(n) | O(n) | 通用场景,代码可读性优先 |
| 数学求和法 | O(n) | O(1) | 数值不大,内存敏感场景 |
| 位运算法 | O(n) | O(1) | 嵌入式/资源受限环境 |
| 排序二分法 | O(n log n) | O(1) | 数据已部分有序或需要复用 |
在真实项目中选择方案时,除了考虑时间复杂度,还需要考虑:
在实际编码中,我遇到过几个典型问题:
完整的测试应该包括:
python复制test_cases = [
([3,0,1], 2),
([0,1], 2),
([9,6,4,2,3,5,7,0,1], 8),
([0], 1),
([], 0),
([1], 0)
]
如果题目改为找出所有缺失数字,我们的方案需要如何调整?
如果数据以流的形式输入,无法存储全部数据,如何解决?
这时数学求和法或位运算法是唯一选择,因为它们只需要O(1)的额外空间。
这类算法在以下场景有实际应用: