边工作边刷题:70天一遍leetcode: day 68
Contains Duplicate I/II/III
要点:I/II都很简单,对于II注意题意是是否在sliding window内存在相等的元素,不是所有相等的都间距小于k。重点说III:对于间距小于k这个条件,还是用sliding window。当检查一个新元素的时候,踢出最老的元素。同时要判断sliding window里有没有在当前元素t范围内的元素。为了找出范围内某个值,需要有序存储的数据结构。同时还要支持元素的insert和delete,所以用treeset tradeoff达到O(lgn)的time complexity。
另一种思路是sliding window里和某个元素满足值范围的只能在3个t大小的bucket里(一个自身桶和2个相邻桶),所以只需存bucket id,这个bucket id可以O(1)时间内计算出来。
- 连续桶 vs. 随机桶:某value所在的bucket id是根据value在数轴上连续范围决定的,所以商作为bucket id。如果要把数轴上连续值分开,则要取模来确定bucket id
- id-1和id+1 bucket: 如果值落在桶m里并且有值,那么可以返回true而中止程序。所以当前桶中要么有唯一值,要么没有值(所以1d map就可以表示)。而m-1和m+1桶也可能和当前值差<=t,所以要多2个检查
- 负轴上的bucket id公式为(val+1)/t-1:why?这么想,-1要对应负轴上的第一个桶,如果按正轴的公式,应该是第0个桶。因为负数不是从0开始,所以整体向左偏移了1,要val+1,得到桶id以后,因为第0个桶实际对应-1,所以要再减1
错误点:
- I很简单,但是不要和single number搞混了,那题是除了一个数之外全相同,所以可以用XOR解
- t和k的理解:为什么要t++?bucket里可能value的个数为t+1即是值差在t范围内,e.g., t=0表示只有一个数,bucket size就是t+1,检查m-1和m+1时t已经++了,所以check是<t。同样对于k,因为是差值at most k,那么sliding window的size是k+1, 所以窗口外元素为i-k-1
- 这题测试蛋疼的加了t<0,直接False,值差最少是0
class Solution(object):
def containsDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
s = set()
for i in nums:
if i in s:
return True
s.add(i)
return False
class Solution(object):
def containsNearbyDuplicate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: bool
"""
s = set()
for i in xrange(len(nums)):
if i>k: s.remove(nums[i-k-1])
if nums[i] in s:
return True
s.add(nums[i])
return False
class Solution(object):
def containsNearbyAlmostDuplicate(self, nums, k, t):
"""
:type nums: List[int]
:type k: int
:type t: int
:rtype: bool
"""
def getId(val, t):
return val/t if val>=0 else (val+1)/t-1
if t<0: return False
hmap = {}
t+=1
for i in xrange(len(nums)):
if i>k: del hmap[getId(nums[i-k-1], t)]
id = getId(nums[i], t)
print id
if id in hmap: return True
if id-1 in hmap and abs(nums[i]-hmap[id-1])<t: return True
if id+1 in hmap and abs(nums[i]-hmap[id+1])<t: return True
hmap[id]=nums[i]
return False