小结
这次共有10道题,主要是学习map、set和dict等数据结构,以及二分查找。
查找的问题的关键是找对数据结构、准确的二分查找套路条件还有诸如持续输出个位数的小技巧。
学习目的
- 完成查找相关经典题:35. 搜索插入位置202. 快乐数205. 同构字符串242. 有效的字母异位词290. 单词规律349. 两个数组的交集350. 两个数组的交集 II 410. 分割数组的最大值451. 根据字符出现频率排序540. 有序数组中的单一元素;
- 学习灵活应用map、set和dict数据结构。
- 活用二分法的套路解题。
数据结构总结
- map(func, struct):作用是对结构struct按顺序以func映射。
- set():无序但不重复的数据结构。
- dict():无序但计数,按键值对结构存储。
map的经典题
205 同构字符串
这道题不关注字母的一致性,偏重于重叠字母的位置要一致。
如何验证各位置是否是重叠字母,只需要找到他们在各自字库的索引(lista.index(str)即可得到str在列表lista中的索引)即可,词各字母的首次出现位置由map可快速映射得到。对比两个词的索引相对位置,则用list(map())结果比较即可。
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
_s = [i for i in s]
_t = [i for i in t]
return list(map(_s.index, _s)) == list(map(_t.index, _t))
290. 单词规律
跟205 同构字符串一样的思路,如果在各自词库中出现的序号一致的话就算相同模式了,所以选择将拆分后的各个字母/词映射为各自词库的索引,然后比较两个词的索引列表是否一致即可。
class Solution:
def wordPattern(self, pattern: str, str: str) -> bool:
pattern = [i for i in pattern]
str = str.split(' ')
return list(map(pattern.index, pattern)) == list(map(str.index, str))
set()的经典题
349. 两个数组的交集
这道题需要的是重复出现的数,不考虑次数,因此选择set数据结构,对比两个数组的去重unique集合值,若同时存在即为交集,也可以直接&求交集。
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
return set(nums1) & set(nums2)
dict()的经典题
242. 有效的字母异位词
这道题不要求字母所在信息,只要相同字母出现相同次数,因此考虑以字典存储(key-value:出现的字母-出现的频次),然后比较两个字典是否一致即可。
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
import collections
obj1 = collections.Counter(s)
obj2 = collections.Counter(t)
return obj1 == obj2
350. 两个数组的交集 II
这道题与349. 两个数组的交集相比,多了一个出现次数的要求,因此考虑以字典存储(key-value:出现的数字-出现的频次),然后输出两个字典均包含的数字,并以较少的那个出现频次输出。
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
import collections
obj1 = collections.Counter(nums1)
obj2 = collections.Counter(nums2)
res = []
for i in obj1:
if i in obj2:
n = min(obj1[i], obj2[i])
res.extend([i]*n)
return res
451. 根据字符出现频率排序I
这道题要求出现的字母+出现的频次并降序排列,因此考虑以出现的字母-出现的频次为键值对的字典保存其词库信息,然后想办法按出现频次降序排列,依次输出出现频次个字母。
注:这道题的tree输出老是错,不知道怎么办。
class Solution:
def frequencySort(self, s: str) -> str:
from collections import Counter
obj = Counter([_ for _ in s])
asd = sorted(obj.items(),key=lambda item:item[1],reverse=True)
return ''.join([i*j for i, j in asd])
二分查找
二分查找的套路如下,关键在1)是否有必要可更改high的初值 2)找到保留右半部分的cond。
low,high = 0, len(nums)-1
while low < high:
mid = (low + high) // 2
if cond: #保留右半部分
low = mid + 1
else:
high = mid
return low
35. 搜索插入位置
- 因为待插入数字可以随便出现在0~len(nums)位置,因此需要修改high的初值为len(nums)。
- 怎么找cond使得保留右半部分?答:当nums[mid]<待插入数字时候,所以待插入数字必然出现在右半部分。
- 然后,题目就写完了,万能的套路啊!
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
lo, hi = 0, len(nums)
while hi > lo:
m = (lo + hi) // 2
if target > nums[m]:
lo = m + 1
else:
hi = m
return lo
540. 有序数组中的单一元素
- 是否有必要可更改high的初值?因为落单的数字可以随便出现在0~len(nums)-1位置,因此不需要修改high的初值。
- 怎么找cond使得保留右半部分?
如果mid为奇数,那么左边0~mid共偶数个数,如果nums[mid-1]与nums[mid]相等,则说明左边都是成对的数,落单的数在右边,要保留右边!
如果mid为偶数,那么左边0~mid共奇数个数,如果nums[mid]与nums[mid+1]相等,则说明左边与nums[mid+1]成双成对,落单的数在右边,要保留右边!
另一个方法我想破脑壳也想不到,抑或使得偶数+=1,奇数-=1,于是乎第一种方法的那么长的废话变成了nums[mid] == nums[mid^1],真是简洁,666. - 然后,题目就写完了,万能的套路啊!
class Solution:
def singleNonDuplicate(self, nums: List[int]) -> int:
'''方法1——mid奇偶性+[mid-1][mid][mid+1]数是否相等'''
lo, hi = 0, len(nums) - 1
while lo < hi:
mid = (lo + hi) // 2
if ((mid %2 ==0) and (nums[mid] == nums[mid+1])) or ((mid %2 !=0) and (nums[mid-1] == nums[mid])):
lo = mid + 1
else:
hi = mid
return nums[lo]
class Solution:
def singleNonDuplicate(self, nums: List[int]) -> int:
'''方法2——mid奇偶性+[mid-1][mid][mid+1]数是否相等居然变成了神奇的抑或,666'''
lo, hi = 0, len(nums) - 1
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] == nums[mid^1]:
lo = mid + 1
else:
hi = mid
return nums[lo]
410. 分割数组的最大值
按值二分?仓促间看不懂,这题延期…
20200826补充。
这道题是按值进行二分:对于给定的数组和最大值,在保证这个最大值的情况下至少要分多少组,如果需要分组数高于输入的m次,说明在分组m次条件下无法实现,需要提高给定的数组和最大值,即选取数组和右侧部分。
- 是否有必要可更改low,high的初值?因为数组和最大值可以随便出现在 m i n ( n u m s ) s u m ( n u m s ) min(nums)~sum(nums) min(nums) sum(nums)之间,当然我也不知道为什么要用 m a x ( n u m s ) s u m ( n u m s ) max(nums)~sum(nums) max(nums) sum(nums)。
- 怎么找cond使得保留右半部分?分组数结果高于输入时候保留。
- 套用套路,完美
class Solution:
def splitArray(self, nums: List[int], m: int) -> int:
def helper(mid):
_sum = 0
n = 0
for i in nums:
if _sum + i <= mid:
_sum += i
else:
_sum = i
n += 1
return n+1
lo, hi = max(nums), sum(nums)
while lo < hi:
mid = (lo + hi) // 2
if helper(mid) > m:
lo = mid + 1
else:
hi = mid
return lo
持续输出个位数+while循环练习+击破无限循环
202. 快乐数
这道题的精髓就是持续输出个位数+while循环练习+击破无限循环。
- %得最后一位数是指n%10得到个位数,配合着n=n//10就能不停地得到个位数
- while循环。
- 击破无限循环的方式就是,是不是曾经出现过?如果出现那循环下去就无休止了,直接返回False。
class Solution:
def isHappy(self, n: int) -> bool:
already = set()
while n != 1:
res = 0
while n > 0:
last = n % 10
n = n // 10
res += last ** 2
if res in already:
return False
else:
already.add(res)
n = res
return True
参考:
- 优秀的datawhalechina/team-learning-program
- leetcode官方及大神的解答