考虑的基本数据结构
第一类:查找有无——set
第二类:查找对应关系(键值对应)——dict
第三类:改变映射关系——map
LeetCode传送门
给定两个数组,编写一个函数来计算它们的交集。
说明:输出结果中的每个元素一定是唯一的。我们可以不考虑输出结果的顺序。
示例 :
思路1:
把nums1记录为set,判断nums2的元素是否在set中,是的话,就放在一个公共的set中,最后公共的set就是我们要的结果。
代码:
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1 = set(nums1)
return set([i for i in nums2 if i in nums1])
思路2:
通过set的内置方法来实现,直接求set的交集。
代码:
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
set1 = set(nums1)
set2 = set(nums2)
return set2 & set1 # 或 return set1.intersection(set2)
LeetCode传送门
给定两个数组,编写一个函数来计算它们的交集。
说明:输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。我们可以不考虑输出结果的顺序。
示例:
思路:
元素出现的次数有用,那么对于存储次数就是有意义的,所以选择数据结构时,就应该选择dict的结构,通过字典的比较来判断;记录每个元素的同时要记录这个元素的频次。
记录num1的字典,遍历nums2,比较nums1的字典的nums的key是否大于零,从而进行判断。
代码:
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
from collections import Counter
nums1_dict=Counter(nums1)
res=[]
for num in nums2:
if nums1_dict[num]>0:
res.append(num)
nums1_dict[num]-=1
return res
LeetCode传送门
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
思路:
判断异位词即判断变换位置后的字符串和原来是否相同,那么不仅需要存储元素,还需要记录元素的个数。可以选择dict的数据结构,将字符串s和t都用dict存储,而后直接比较两个dict是否相同。
代码:
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
from collections import Counter
s=Counter(s)
t=Counter(t)
if s==t:
return True
else:
return False
LeetCode传送门
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False 。
示例:
思路:
重复执行「将该数替换为它每个位置上的数字的平方和」这一过程,猜测会有以下三种可能:
第三个情况比较难以检测和处理。考虑各个位数的最大数字的下一位数是多少:
Digits | Largest | Next |
---|---|---|
1 | 9 | 81 |
2 | 99 | 162 |
3 | 999 | 243 |
4 | 9999 | 324 |
13 | 9999999999999 | 1053 |
对于 33 位数的数字,它不可能大于 243243。这意味着它要么被困在 243243 以下的循环内,要么跌到 11。44 位或 44 位以上的数字在每一步都会丢失一位,直到降到 33 位为止。所以我们知道,最坏的情况下,算法可能会在 243243 以下的所有数字上循环,然后回到它已经到过的一个循环或者回到 11。但它不会无限期地进行下去,所以我们排除第三种选择。
这样这道题的思路就很明显了,当 n 不等于 1 时就循环,每次循环时,将其最后一位到第一位的数依次平方求和,比较求和是否为1。那么这里也可以按此判断,因为只需要判断有或无,不需要记录次数,故用set的数据结构。每次对求和的数进行add,当新一次求和的值存在于set中时,就return false.
代码:
class Solution:
def isHappy(self, n: int) -> bool:
exists=set()
while n!=1:
sums=0
while n>0:
tmp=n%10
sums+=tmp**2
n//=10
if sums in exists:
return False
else:
exists.add(sums)
n=sums
return True
LeetCode传送门
给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。你可以假设 pattern 只包含小写字母, str 包含了由单个空格分隔的小写字母。
示例:
思路:
如何能既考虑顺序,也考虑 p a t t e r n pattern pattern 和 s t r str str 键值对应的关系呢?
抓住变与不变,变的是键,但是不变的是各个字典中,对应的相同 i n d e x index index 下的值,如 d i c t 1 [ i n d e x ] = d i c t 2 [ i n d e x ] dict1[index] = dict2[index] dict1[index]=dict2[index],那么我们可以创建两个新的字典,遍历index对两个新的字典赋值,并比较value。
还有一个思路比较巧妙,既然不同,那么可以考虑怎么让它们相同,将原来的dict通过map映射为相同的key,再比较相同key的dict是否相同。
代码:
注:map是通过hash存储的。
class Solution:
def wordPattern(self, pattern: str, str: str) -> bool:
str=str.split()
return list(map(pattern.index,pattern))==list(map(str.index,str))
LeetCode传送门
给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。你可以假设 s 和 t 具有相同的长度。
示例 1:
示例 2:
思路:
可以考虑通过建两个dict,比较怎样不同,也可以将不同转化为相同。
代码:
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
n=len(s)
st,ts={
},{
}
for i in range(n):
if s[i] in st:
if t[i]!=st[s[i]]:
return False
if t[i] in ts:
if s[i]!=ts[t[i]]:
return False
st[s[i]]=t[i]
ts[t[i]]=s[i]
return True
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
return list(map(s.index,s)) == list(map(t.index,t))
LeetCode传送门
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
示例 :
思路:
使用字典统计频率,对字典的value进行排序,最终根据key的字符串乘上value次数,组合在一起输出。
代码:
class Solution:
def frequencySort(self, s: str) -> str:
from collections import Counter
s_dict=Counter(s)
# sorted 返回的是列表元组
s=sorted(s_dict.items(),key=lambda item:item[1],reverse=True)
ans=''
for k,v in s:
ans+=k*v
return ans
当我们需要枚举数组中的两个元素时,如果我们发现随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法,将枚举的时间复杂度从 O ( n 2 ) O(n^2) O(n2) 减少至 O ( n ) O(n) O(n)。
LeetCode传送门
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
思路1: 暴力法 O ( n 2 ) O(n^2) O(n2)
第一遍遍历数组,第二遍遍历当前遍历值之后的元素,其和等于 target 则 return。
代码:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
n=len(nums)
for i in range(n):
for j in range(i+1,n):
if nums[i]+nums[j]==target:
return [i,j]
思路2: 排序+指针对撞 O ( n ) + O ( n log n ) = O ( n ) O(n)+O(n\log n)=O(n) O(n)+O(nlogn)=O(n)
因为问题本身不是有序的,因此需要对原来的数组进行一次排序,排序后就可以用 O ( n ) O(n) O(n)的指针对撞进行解决。
但是问题是,返回的是数字的索引,如果只是对数组的值进行排序,那么数组原来表示的索引的信息就会丢失,所以在排序前要进行些处理。
list(enumerate(nums))
开始实现下标和值的绑定。代码:
class Solution:
def twoSum(self,nums,target):
nums=list(enumerate(nums))
nums.sort(key=lambda x:x[1])
l,r=0,len(nums)-1
while l<r:
if nums[l][1]+nums[r][1]>target:
r-=1
elif nums[l][1]+nums[r][1]<target:
l+=1
else:
return nums[l][0],nums[r][0]
思路3: 查找表 O ( n ) O(n) O(n)
遍历数组过程中,当遍历到元素v时,可以只看v前面的元素,是否含有target-v的元素存在。
即使 v 放在了之前的查找表中覆盖了 v,也不影响当前 v 元素的查找。因为只需要找到两个元素,只需要找 target-v 的另一个元素即可。
代码:
class Solution:
def twoSum(self,nums,target):
record=dict()
for i in range(len(nums)):
complement=target-nums[i]
if record.get(complement) is not None:
return i,record[complement]
record[nums[i]]=i
LeetCode传送门
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路:
[0, 1, 2, 2, 2, 3]
,使用三重循环枚举到的第一个三元组为 (0,1,2),如果第三重循环继续枚举下一个元素,那么仍然是三元组 (0,1,2),产生了重复。因此我们需要将第三重循环「跳到」下一个不相同的元素,即数组中的最后一个元素 33,枚举三元组 (0, 1, 3)(0,1,3)。代码:
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
ans=[]
for i in range(len(nums)-2):
# 因为是排序好的数组,如果最小的都大于0可以直接排除
if nums[i]>0:break
# 排除i的重复值
if i>0 and nums[i]==nums[i-1]:continue
l,r=i+1,len(nums)-1
while l<r:
sums=nums[i]+nums[l]+nums[r]
if sums==0:
ans.append([nums[i],nums[l],nums[r]])
l+=1
r-=1
while l<r and nums[l]==nums[l-1]:l+=1
while l<r and nums[r]==nums[r+1]:r-=1
elif sums<0:
l+=1
else:
r-=1
return ans
总结:
# 1.
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i-1]: continue
# 2.
while l < r:
while l < r and nums[l] == nums[l-1]: l += 1
LeetCode传送门
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
思路:
4Sum可以当作是3Sum问题的扩展,注意事项仍是一样的,同样是不能返回重复值得解。首先排序。接着从 [ 0 , l e n − 1 ] [0,len- 1] [0,len−1]遍历 i i i,跳过 i i i 的重复元素,再在 [ i + 1 , l e n − 1 ] [i+1,len-1] [i+1,len−1] 中遍历 j j j,得到 i , j i,j i,j后,再选择首尾的 l l l 和 r r r,通过对撞指针的思路,四数和大的话 r − − r-- r−−,小的话 l + + l++ l++,相等的话纳入结果 list,最后返回。
另外,需要加些边界条件判断:当len小于4时,直接返回;当只有4个值且长度等于target时,直接返回本身即可。
代码:
Python标准库itertools.combinations:超出时间限制
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
from itertools import combinations
ans=set()
for i in combinations(nums,4):
if sum(i)==target:
ans.add(i)
return list(ans)
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
from itertools import combinations
res = []
for i in combinations(nums, 4):
if sum(i) == target:
res.append(i)
res = set(res)
return res
>> from itertools import combinations, permutations
>> permutations([1, 2, 3], 2)
<itertools.permutations at 0x7febfd880fc0>
>> list(permutations([1, 2, 3], 2))
[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
>> list(combinations([1, 2, 3], 2))
[(1, 2), (1, 3), (2, 3)]
LeetCode传送门
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。 3 ≤ n u m s . l e n g t h ≤ 1 0 3 , − 1 0 3 ≤ n u m s [ i ] ≤ 1 0 3 , − 1 0 4 ≤ t a r g e t ≤ 1 0 4 3\leq nums.length\leq 10^3,-10^3\leq nums[i]\leq 10^3,-10^4\leq target\leq 10^4 3≤nums.length≤103,−103≤nums[i]≤103,−104≤target≤104。
示例:
思路:
开始时可以随机设定一个三个数的和为结果值,在每次比较中,先判断三个数的和是否和target相等,如果相
等直接返回和。如果不相等,则判断三个数的和与target的差是否小于这个结果值时,如果小于则进行则进行替
换,并保存和的结果值。
代码:
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
nums.sort()
ans=nums[0]+nums[1]+nums[2]
diff=abs(ans-target)
for i in range(len(nums)):
l,r=i+1,len(nums)-1
t=target-nums[i]
while l<r:
if nums[l]+nums[r]==t:
return nums[i]+t
else:
if abs(nums[l]+nums[r]-t)<diff:
diff=abs(nums[l]+nums[r]-t)
ans=nums[i]+nums[l]+nums[r]
if nums[l]+nums[r]<t:
l+=1
else:
r-=1
return ans
LeetCode传送门
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。
思路1: O ( n 3 ) O(n^3) O(n3)
首先可以考虑把D数组中的元素都放入查找表,然后遍历前三个数组,判断target减去每个元素后的值是否在查找表中存在,存在的话,把结果值加1。那么查找表的数据结构选择用set还是dict?考虑到数组中可能存在重复的元素,而重复的元素属于不同的情况,因此用dict存储,最后的结果值加上dict相应key的value。
代码:
class Solution:
def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:
from collections import Counter
record=Counter(D)
ans=0
for i in range(len(A)):
for j in range(len(B)):
for k in range(len(C)):
num_find=0-A[i]-B[j]-C[k]
if record.get(num_find)!=None:
ans+=record[num_find]
return ans
思路2:
对于C和D的数组,可以通过dict来记录其中和的个数,之后遍历结果在和中进行查找
代码:
class Solution:
def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:
from collections import Counter
record=Counter()
for i in range(len(A)):
for j in range(len(B)):
record[A[i]+B[j]]+=1
ans=0
for i in range(len(C)):
for j in range(len(D)):
num_find=0-C[i]-D[j]
if record.get(num_find)!=None:
ans+=record[num_find]
return ans
class Solution:
def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:
record = collections.Counter(a + b for a in A for b in B)
return sum(record.get(- c - d, 0) for c in C for d in D)
LeetCode传送门
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
所有输入均为小写字母。不考虑答案输出的顺序。
示例:
思路:
题目中不变的条件:如果将字符串统一排序,异位词排序后的字符串,显然都是相同的。
可以把异位词排序后的字符串当作key,把遍历的数组中的异位词当作value,对字典进行赋值,进而遍历字典的value,得到结果list。
代码:
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
from collections import defaultdict
strs_dict=defaultdict(list)
ans=[]
for s in strs:
key=''.join(sorted(list(s)))
strs_dict[key]+=s.split(',')
for v in strs_dict.values():
ans.append(v)
return ans
defaultdict:字典的子类。
defaultdict与dict实例化字典类型的区别:使用defaultdict,任何未定义的key都会默认返回一个根据method_factory参数不同的默认值, 而相同情况下dict()会返回KeyError。
LeetCode传送门
给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺序)。
找到所有回旋镖的数量。你可以假设 n 最大为 500,所有点的坐标在闭区间 [-10000, 10000] 中。
示例:
思路:
开始的思路就是三层遍历,i从0到len,j从i+1到len,k从j+1到len,然后比较三个点的距离,相等则结果数加一,这样的时间复杂度为 O ( n 3 ) O(n^3) O(n3)。考虑采用查找表的思路进行优化。
考虑在这道题中,可以通过查找表进行代替哪两层循环。当 i , j i,j i,j 两点距离等于 i , k i,k i,k 时,用查找表的思路,等价于:对距离 key (i,j或i,k的距离),其值 value (个数)为2。那么就可以做一个查找表,用来查找相同距离 key 的个数 value 是多少。遍历每一个节点i,扫描得到其他点到节点 i 的距离,在查找表中,对应的键就是距离的值,对应的值就是距离值得个数。在拿到对于元素i的距离查找表后,接下来就是排列选择问题了:当距离为 x x x 的值有 n ( n ≤ 2 ) n(n\leq2) n(n≤2) 个时,选择 j , k j,k j,k 的可能情况有:第一次选择有 n n n 种,第二次选择有 n − 1 n-1 n−1 种。
对于距离值的求算,按照欧式距离的方法进行求算的话,容易产生浮点数,可以将根号去掉,用差的平方和来进行比较距离。
代码:
class Solution:
def numberOfBoomerangs(self, points: List[List[int]]) -> int:
ans=0
from collections import Counter
for i in points:
record=Counter()
for j in points:
if i!=j:
record[self.dis(i,j)]+=1
for k,v in record.items():
ans+=v*(v-1)
return ans
def dis(self,point1,point2):
return (point1[0]-point2[0])**2+(point1[1]-point2[1])**2
class Solution:
def numberOfBoomerangs(self, points: List[List[int]]) -> int:
from collections import Counter
def f(x1,y1):
d=Counter((x2-x1)**2+(y2-y1)**2 for x2,y2 in points)
return sum(t*(t-1) for t in d.values())
return sum(f(x1,y1) for x1,y1 in points)
LeetCode传送门
给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。
示例:
思路:
判断点是否在一条直线上,其实就等价于判断 i , j i,j i,j 两点的斜率是否等于 i , k i,k i,k 两点的斜率。
直接考虑使用查找表实现,即查找相同斜率 key 的个数 value 是多少。
代码:
class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
if len(points)<=1:return len(points)
ans=0
from collections import defaultdict
for i in range(len(points)):
record=defaultdict(int)
samepoint=0
for j in range(len(points)):
if points[i]==points[j]: # 包括j==i
samepoint+=1
else:
record[self.get_slope(points,i,j)]+=1
for v in record.values():
ans=max(ans,v+samepoint)
ans=max(ans,samepoint)
return ans
def get_slope(self,points,i,j):
from decimal import Decimal
if points[i][0]-points[j][0]==0:
return float('inf')
else:
return Decimal(str((points[i][1]-points[j][1])))/Decimal(str((points[i][0]-points[j][0])))
class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
if len(points)<=1:return len(points)
ans=0
from collections import defaultdict
for i in range(len(points)):
record=defaultdict(int)
samepoint=0
for j in range(len(points)):
if points[i]==points[j]: # 包括j==i
samepoint+=1
else:
record[self.get_slope(points,i,j)]+=1
for v in record.values():
ans=max(ans,v+samepoint)
ans=max(ans,samepoint)
return ans
def gcd(self,dy,dx):
if y==0:
return x
else:
return gcd(y,x%y)
def get_slope(self,points,i,j):
dy=points[i][1]-points[j][1]
dx=points[i][0]-points[j][0]
g=gcd(dy,dx)
if g!=0:
dy//=g
dx//=g
return '{}/{}'.format(dy,dx)
滑动窗口是数组/字符串问题中常用的抽象概念。 窗口通常是在数组/字符串中由开始和结束索引定义的一系列元素的集合,即 [,) 。滑动窗口是可以将两个边界向某一方向“滑动”的窗口。
LeetCode传送门
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例:
思路1: 滑动窗口
使用 HashSet 将字符存储在当前窗口 [,) (最初 = )中,然后向右侧滑动索引 ,如果它不在 HashSet 中,我们会继续滑动 ,直到 [] 已经存在于 HashSet 中。此时,找到的没有重复字符的最长子字符串将会以索引 开头,如果对所有的 这样做,就可以得到答案。
代码:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
n=len(s)
HashSet=set([])
ans,i,j=0,0,0
while i<n and j<n:
if s[j] not in HashSet:
HashSet.add(s[j])
j+=1
ans=max(ans,j-i)
else:
HashSet.remove(s[i])
i+=1
return ans
思路2: 哈希表优化的滑动窗口
上述的方法最多需要执行 2n 个步骤。事实上,它可以被进一步优化为仅需要 n 个步骤。定义字符到索引的映射,而不是使用集合来判断一个字符是否存在。当找到重复的字符时,可以立即跳过该窗口。也就是说,如果 [] 在 [,) 范围内有与 ′ 重复的字符,不需要逐渐增加 ,可以直接跳过 [,′] 范围内的所有元素,并将 变为 ′+1 。
代码:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
n=len(s)
ans,i=0,0
HashMap={
}
for j in range(n):
if s[j] in HashMap:
i=max(HashMap.get(s[j],-1),i)#窗口左端变为j'+1(index)
ans=max(ans,j-i+1)# [j'+1,j]字符长度
HashMap[s[j]]=j+1# key为字符,value为j'+1
return ans
LeetCode传送门
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。
思路:
固定滑动数组的长度为K+1,当这个滑动数组内如果能找到两个元素的值相等,就可以保证两个元素的索引的差是
小于等于k的。如果当前的滑动数组中没有元素相同,就右移滑动数组的右边界r,同时将左边界l右移。查看r++的元
素是否在l右移过后的数组里,如果不在就将其添加数组,在的话返回true表示两元素相等。
因为滑动数组中的元素是不同的,考虑用set作为数据结构。
代码:
class Solution:
def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
record=set()
for i in range(len(nums)):
if nums[i] in record:
return True
record.add(nums[i])
if len(record)==k+1:
record.remove(nums[i-k])
return False
LeetCode传送门
在整数数组 nums 中,是否存在两个下标 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值小于等于 t ,且满足 i 和 j 的差的绝对值也小于等于 ķ 。如果存在则返回 true,不存在返回 false。
思路:
代码:
class Solution:
def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
record=set()
for i in range(len(nums)):
if len(record)!=0:
rec=list(record)
ix=self.lower_bound(rec,nums[i]-t)
if ix!=-1 and rec[ix]<=nums[i]+t:
return True
record.add(nums[i])
if len(record)==k+1:
record.remove(nums[i-k])
return False
# 大于target的最小元素
def lower_bound(self,nums,target):
low,high=0,len(nums)-1
while low<high:
mid=(low+high)//2
if nums[mid]<target:
low=mid+1
else:
high=mid
return low if nums[low]>=target else -1
事实上,暴力遍历的运行时间要比上面的小,原因可能是上面的步骤中存在着大量的set和list的转换导致。
class Solution:
def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
if t==0 and len(nums)==len(set(nums)):
return False
for i in range(len(nums)):
for j in range(1,k+1):
if i+j>=len(nums):break
if abs(nums[i+j]-nums[i])<=t:return True
return False
二分查找代码模板:
def binarySearch (arr, x):
n=len(arr)
low,high=0,n-1
while low<high:
mid=(low+high)//2
if f(x):
low=mid+1
else:
high=mid
return low
LeetCode传送门
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。
思路:
high 要设置为 len(nums) 的原因是target可能大于数组的最大值。
代码:
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
low,high=0,len(nums)
while low<high:
mid=(low+high)//2
if nums[mid]<target:
low=mid+1
else:
high=mid
return low
LeetCode传送门
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
注意:您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。
思路:
代码:
class Solution:
def singleNonDuplicate(self, nums: List[int]) -> int:
low,high=0,len(nums)-1
while low<high:
mid=(low+high)//2
if nums[mid]==nums[mid^1]:
low=mid+1
else:
high=mid
return nums[low]
LeetCode传送门
给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。注意:数组长度 n 满足以下条件:1 ≤ n ≤ 1000,1 ≤ m ≤ min(50, n)
思路:
代码:
class Solution:
def splitArray(self, nums: List[int], m: int) -> int:
def helper(mid):
ans=tmp=0
for num in nums:
if tmp+num<=mid:
tmp+=num
else:
ans+=1
tmp=num
return ans+1
low,high=max(nums),sum(nums)
while low<high:
mid=(low+high)//2
if helper(mid)>m:
low=mid+1
else:
high=mid
return low
Datawhale社区开源教程之leetcode编程实践
LeetCode题解