数组-剑指offer3数组中重复的数字-简单-20210806
1. 题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任一一个重复的数字。
示例:
例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1
2. 题目解答
2.1 第一次尝试答案-原地交换
时间复杂度:O(n)
空间复杂度:O(1)
# @param numbers int整型一维数组
# @return int整型
class Solution:
def duplicate(self, numbers):
# write code here
# illegal input
if not numbers or len(numbers) <= 0:
return -1
for i in range(len(numbers)):
if numbers[i] < 0 or numbers[i] >= len(numbers): # 注意是大于等于
return -1
# find duplicate number
for i in range(len(numbers)):
while numbers[i] != i:
# 相等,在下表i和m均有出现
if numbers[i] == numbers[numbers[i]]:
return numbers[i]
# 不等,交换索引和值对应的索引位置
else:
temp = numbers[i]
numbers[i] = numbers[temp]
numbers[temp] = temp
return -1 # 需要考虑没有重复数字时候的情况
2.2 第二种解法(较优)
- ① 初始化: 新建 HashSet ,记为 dic ;
- ② 遍历数组 nums中的每个数字 num :
- 当num不在dic中,将 num 添加至 dic 中;
- 当 num 在 dic 中,说明重复,直接返回 num
- ③ 最后返回 -1,说明无重复数字
时间复杂度:O(n)
空间复杂度:O(n)
class Solution:
def findRepeatNumber(self, nums):
dic = set()
for num in nums:
if num in dic: return num
dic.add(num)
return -1
2.3 第三种解法(效率差)
- ① 先把输入的数组排序 O(nlogn) 快排复杂度
- ② 从头扫描找到重复数组
时间复杂度:O(nlogn)
空间复杂度:O(n)
class Solution:
def findRepeatNumber(self, nums):
# 排序
nums.sort()
# 找重复数字
temp = 0
for i in range(1, len(nums)):
if nums[i] == nums[i-1]:
return nums[i]
return -1
3. 测试用例
1. 空数组
2. 数组内数字不在1-n范围内,小于1或大于n两种可能
3. 特殊数组
4. 包含1个或多个重复数字
5. 不包含重复数字
数组-剑指offer3数组中重复的数字(不修改数组)
1. 题目描述:
不修改数组找出重复数字
在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。
示例
如果输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},那么对应的输出是重复的数字2或3
2. 题目解答
2.1 同上解法2 创建一个新数组
空间占用高
时间复杂度:O(n)
空间复杂度:O(n)
class Solution(object):
def findRepeatNumber(self, nums):
dic = set()
for num in nums:
if num in dic: return num
dic.add(num)
return -1
2.2 二分法
以时间换空间
时间复杂度:O(nlogn)
空间复杂度:O(1)
思路:
会有一个缺点:不能找出所有重复的数字,如1~2范围内2出现2次,而1不出现,该算法不能判别
def countRange(self, numbers, length, start, end):
count = 0
for i in range(length):
if (numbers[i] >= start) and (numbers[i] <= end):
count += 1
return count
def dichotomy(self, numbers):
'''
array: n+1
range: 1~n
'''
if not numbers or len(numbers) <= 0:
return -1
for num in numbers:
if num < 1 or num >= len(numbers): return -1
# find duplicate numbers
start, end = 1, len(numbers)-1
while end >= start:
middle = (start + end) // 2
count = self.countRange(numbers, len(numbers), start, middle)
if end == start:
if count > 1:
return start
else:
break
if count > (middle - start + 1):
end = middle
else:
start = middle + 1
return -1
# !/usr/bin/env python
# --*--coding:utf-8-*-
__author__ = 'Xue Wang'
# @param numbers int整型一维数组
# @return int整型
class Solution(object):
def countRange(self, nums, l, r):
count = 0
for i in range(len(nums)):
if l <= nums[i] <= r: # bug:<= 写错了
count += 1
return count
def findDuplicate(self, nums):
if not nums or len(nums) <= 0:
return -1
for num in nums:
if num < 1 or num >= len(nums):
return -1
# find duplicate numbers
l, r = 1, len(nums)-1
while r >= l:
mid = (r + l) // 2
count = self.countRange(nums, l, mid)
if l == r:
if count > 1: return l
else: break
if count > mid - l + 1:
r = mid
else:
l = mid + 1
return -1
if __name__ == '__main__':
# Test Case 0~n-1
# 注意测试用例范围,否则会溢出
nums_1 = [] # 空数组
nums_2 = [1] # 只有一个值
nums_3 = [-1, 0] # 元素不符合
nums_4 = [1, 2, 0] # 不包含重复数字
nums_5 = [1, 2, 3, 4, 3]
nums_6 = [2, 2, 5, 4, 3] # no pass
nums_7 = [2, 2, 5, 9, 3] # 超出规定范围
Solution = Solution()
print(Solution.findDuplicate(nums_1))
print(Solution.findDuplicate(nums_2))
print(Solution.findDuplicate(nums_3))
print(Solution.findDuplicate(nums_4))
print(Solution.findDuplicate(nums_5))
print(Solution.findDuplicate(nums_6))
print(Solution.findDuplicate(nums_7))
2.3 二分法-递归(错解)
class Solution:
def findDuplicate(self,num,l,r):
if num == None or len(num) <= 0: # 排除空数组
return False
for i in range(len(num)):
if num[i] < 0 or num[i] >= len(num): # 若数组元素不在1到n,也排除
return False
if l == r: # 如果区间左右指针相等,说明已经找到重复元素,直接返回
return l
mid = (l + r) // 2 # 中间元素
count = 0 # 计数变量
for i in range(len(num)): # 遍历
if l <= num[i] <= mid: # 如果数组元素出现在 1 到 mid 之间,计数变量加1
count += 1
if count > mid - l + 1: # 若计数变量大于1 到 mid之间元素的个数,说明重复数字在1到 mi区间内。
return loop_search(num, l, mid) # 则更新区间,继续寻找重复元素
else:
return loop_search(num, mid+1, r) # 反之亦然
if __name__ == '__main__':
# 验证
# 长度为n的数组里包含一个或多个重复的数字。
test_1 = [2,3,5,4,3,2,6,7]
# 数组中不包含重复的数字
test_2 = [1,5,3,2,4]
# 空数组
test_3 = None
# 数组元素超出要求
test_4 = [4,3,2,9]
solution = Solution()
print("test_1:", solution.re_search(test_1,1,len(num)))
print("test_2:", solution.re_search(test_2,1,len(num)))
print("test_3:", solution.re_search(test_3,1,len(num)))
print("test_4:", solution.re_search(test_4,1,len(num)))