庖丁解牛剑指 Offer 03 找到数组中重复的数字,窥探算法之美
2023-12-05 11:53:32
剑指 Offer 03:找出数组中的重复数字
剑指 Offer 03 题给定一个长度为 n 的数组 nums,其中所有数字都在 0~n-1 范围内,并且数组中某些数字是重复的。要求找出数组中任意一个重复的数字。
1. 暴力遍历(双重循环)
最直接的解法是使用暴力遍历法,通过两个 for 循环来比较数组中的每个数字与其他所有数字,一旦发现重复的数字,就立即返回。这种方法的时间复杂度为 O(n^2),空间复杂度为 O(1)。
def find_duplicate(nums):
for i in range(len(nums)):
for j in range(i+1, len(nums)):
if nums[i] == nums[j]:
return nums[i]
2. 哈希表(字典)
使用哈希表(字典)来存储数组中的数字,如果一个数字已经存在于哈希表中,则说明它是重复的,直接返回即可。这种方法的时间复杂度为 O(n),空间复杂度也为 O(n)。
def find_duplicate(nums):
hash_table = {}
for num in nums:
if num in hash_table:
return num
else:
hash_table[num] = True
3. 原地修改数组
我们可以通过修改数组中的元素来实现重复数字的查找。具体来说,我们将遍历数组中的每个数字,并将其作为下标来访问数组。如果当前数字作为下标访问到的数字等于当前数字,则说明当前数字是重复的,直接返回即可。这种方法的时间复杂度为 O(n),空间复杂度为 O(1)。
def find_duplicate(nums):
for i in range(len(nums)):
index = nums[i] % len(nums)
if nums[index] == nums[i]:
return nums[i]
else:
nums[index] = nums[i]
4. 位运算(异或)
位运算中的异或运算(XOR)具有以下性质:
- a XOR a = 0
- a XOR b XOR a = b
- a XOR b = b XOR a
我们可以利用这些性质来解决本题。首先将数组中的所有数字异或一遍,然后将结果与数组中的每个数字异或一遍,如果结果为 0,则说明该数字是重复的。这种方法的时间复杂度为 O(n),空间复杂度为 O(1)。
def find_duplicate(nums):
xor_result = 0
for num in nums:
xor_result ^= num
for num in nums:
xor_result ^= num
return xor_result
5. 二分查找
我们还可以使用二分查找算法来解决本题。首先对数组进行排序,然后在排序后的数组中进行二分查找。如果当前二分到的数字等于二分到数字的前一个数字,则说明当前二分到的数字是重复的,直接返回即可。这种方法的时间复杂度为 O(n log n),空间复杂度为 O(1)。
def find_duplicate(nums):
nums.sort()
low, high = 0, len(nums) - 1
while low <= high:
mid = (low + high) // 2
if nums[mid] == nums[mid-1]:
return nums[mid]
elif nums[mid] > nums[mid-1]:
low = mid + 1
else:
high = mid - 1
return -1
6. 性能分析
以上列出的五种方法的时间复杂度和空间复杂度如下:
方法 | 时间复杂度 | 空间复杂度 |
---|---|---|
暴力遍历 | O(n^2) | O(1) |
哈希表 | O(n) | O(n) |
原地修改数组 | O(n) | O(1) |
位运算 | O(n) | O(1) |
二分查找 | O(n log n) | O(1) |
在实际应用中,哈希表、原地修改数组和位运算方法都是比较高效的,因为它们的时间复杂度都为 O(n),而空间复杂度为 O(1)。在数组规模较大的情况下,这三种方法的性能优势会更加明显。
7. 总结
剑指 Offer 03 题的目的是查找数组中重复的数字。这个问题可以用多种方法来解决,包括暴力遍历、哈希表、原地修改数组、位运算和二分查找。这些方法的时间复杂度和空间复杂度各不相同,在实际应用中,需要根据具体的场景选择合适的方法。