最近刷力扣总能碰到各种关于数组数字元素的题目,而且这些题基本都有很多巧妙的多重解法,博主自己想不出,只能靠大量的记录了。
287. 寻找重复数
不考虑题目的限制条件,立马想到的解法是:
①边遍历边加入set, 直到出现一个原本就存在set中的元素
②遍历并交换数组元素到相应位置上[数字x应填入下标x-1中], 直到出现一个元素与相应位置相等, 就找到了重复元素了
但是以上解法1需要用到额外的o(n)空间, 解法2需要修改原数组, 都不满足条件…这时猪脑过载了, 只能去看大佬解析
解法一: 二分查找
仔细推敲后可以发现如果对1至n的元素x, 去统计nums中小于等于x的元素个数, 记为cnt(x)。 假设重复元素是target, 那么对于target前的那些元素, cnt(x)一定是小于等于x的, 当x恰好为target时, cnt(target) > target, 并且对于x>target的那些元素, 都有cnt(x) > x, 那么二分的性质就出来了,target是界: 问题转化为找到第一个cnt(x) > x 的元素, 该元素就为重复元素。更为细致的证明手推如下:
解法二: 另一种更普适的二分
另一种二分思路是参考剑指offer书P41,若区间[l, r]有重复元素,统计[l, mid]的元素个数,若个数大于区间数则说明该段必包含重复元素,否则另一段含重复元素。
这种方法还适用于多重复元素的情形,但最终只能找到其中一个重复元素。
注意:多重复元素不能用第一种二分,因为cnt(i)-i的单调性质不能保证
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
def countNums(numbers, m, n):
return sum(True for number in numbers if m<=number<=n)
st, ed = 1, len(nums)-1
while st < ed:
mid = (st+ed)//2
if countNums(nums, st, mid) > mid-st+1:
ed = mid
else:
st = mid + 1
return st
解法三: 数组成环+找环的入口
建立下标和值的映射关系 i -> nums[i], 形成链表。以数组 [1,3,4,2]为例, 形成了 0->1->3->2->4, 如果数组中有重复的数,以数组 [1,3,4,2,2]为例, 形成了0->1->3->2->4->2->4->2->……如下
反证法可以知道有环就一定有重复, 但有重复就一定有环吗? 是的。因为这种做法会倾向于遍历全部元素, 直到找到一个重复元素形成环, 注意: 形成环后不一定能遍历完所有元素了。
1.数组中有一个重复的整数 <> 链表中存在环
2.找到数组中的重复整数 <> 找到链表的环入口
那么问题就变成了环形链表找入口问题了:
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
fast, slow = 0, 0
while True:
fast = nums[nums[fast]]
slow = nums[slow]
if fast == slow:
break
## 相遇
slow = 0
while fast != slow:
fast = nums[fast]
slow = nums[slow]
return fast
JZ3 数组中重复的数字
交换法[原地哈希]
class Solution:
def duplicate(self , numbers: List[int]) -> int:
# write code here
for i in range(len(numbers)):
while numbers[i] != i:
if numbers[i] == numbers[numbers[i]]:
return numbers[i]
numbers[numbers[i]], numbers[i] = numbers[i], numbers[numbers[i]]
return -1
268. 丢失的数字
交换法[原地哈希]
数学法
异或
数组nums 中有 n 个数,在这 n 个数的后面添加从 0 到 n 的每个整数,则添加了 n+1
个整数,共有 2n+1 个整数。
在 2n+1 个整数中,丢失的数字只在后面 n+1个整数中出现一次,其余的数字在前面 n 个整数中(即数组中)和后面n+1 个整数中各出现一次,即其余的数字都出现了两次。
class Solution:
def missingNumber(self, nums: List[int]) -> int:
xor = 0
for i, num in enumerate(nums):
xor ^= i ^ num
return xor ^ len(nums)