剑指offer03. 数组中重复的数字
(列表表示数组,涉及用哈希表计数)
方法一:使用哈希表(dict/set)计数
- 看到别人的题解大多用的是set,但感觉set的索引效率并不是最高的,至少我自己提交后的结果里dict的内存使用和时间都更优
class Solution:
def findRepeatNumber(self, nums: List[int]) -> int:
counts = {}
for num in nums:
if num in counts.keys():
return num
else:
counts[num] = None # 只是转成一种遍历效率更高的数据结构
- 复杂度分析
- 时间复杂度: O ( n ) O(n) O(n)。遍历数组一遍。使用哈希集合(HashSet),添加元素的时间复杂度为 O ( 1 ) O(1) O(1),故总的时间复杂度是 O ( n ) O(n) O(n)。
- 空间复杂度: O ( n ) O(n) O(n)。不重复的每个元素都可能存入集合,因此占用 O ( n ) O(n) O(n)额外空间。
方法二:原地交换
- 题目说明中包含在一个长度为 n 的数组 nums 里的所有数字都在 0 ~ n-1 的范围内这一信息。 此说明含义:数组元素的索引和值是一对多的关系。
- 遍历中,第一次遇到数字 xx 时,将其交换至索引 xx 处;而当第二次遇到数字 xx 时,一定有 nums[x] = xnums[x]=x ,此时即可得到一组重复数字。
class Solution:
def findRepeatNumber(self, nums: [int]) -> int:
i = 0
while i < len(nums):
if nums[i] == i: # 确认index上的值是index就跳过
i += 1
continue
if nums[nums[i]] == nums[i]:
return nums[i]# 如果index上已经有一个等于index的值,意味着重复值出现了
nums[nums[i]], nums[i] = nums[i], nums[nums[i]] # 交换位置,换到index上去
return -1
- 复杂度分析
- 时间复杂度 O ( N ) O(N) O(N): 遍历数组使用 O ( N ) O(N) O(N),每轮遍历的判断和交换操作使用 O ( 1 ) O(1) O(1)。
- 空间复杂度 O ( 1 ) O(1) O(1): 使用常数复杂度的额外空间。
剑指offer53. 在排序数组中查找数字I
方法一:遍历列表进行比较
class Solution:
def search(self, nums: List[int], target: int) -> int:
count = 0
for num in nums:
if num == target:
count += 1
return count
忽视了列表是排序的这一可利用的条件
方法二:二分法
(解决排序数组中的搜索问题用二分法)
排序数组中的所有数字target形成一个窗口,它的左右边界分别为left和right(窗口外的第一个元素),则target的数量为right-left-1。
应用两次二分法分别查找左右边界。为了简化,将二分法封装成函数,转而寻找target和target的右边界
class Solution:
def search(self, nums: [int], target: int) -> int:
def helper(tar):
i, j = 0, len(nums) - 1
while i <= j:
m = (i + j) // 2
if nums[m] <= tar: i = m + 1
else: j = m - 1
return i
return helper(target) - helper(target - 1)
- 复杂度分析:
- 时间复杂度 O ( l o g N ) O(log N) O(logN): 二分法为对数级别复杂度。
- 空间复杂度 O ( 1 ) O(1) O(1) : 几个变量使用常数大小的额外空间。