元素’a’是否存在,通常用set:集合
set只存储键,而不需要对应其相应的值。
set中的键不允许重复
元素’a’出现了几次:dict–>字典
dict中的键不允许重复
通过将原有序列的关系映射统一表示为其他
参考资料:
https://github.com/datawhalechina/team-learning-program/blob/master/LeetCodeClassification/3.%E6%9F%A5%E6%89%BE.md
349. 两个数组的交集(简单)
输出结果中的每个元素唯一,即不需要考虑元素出现的次数,用set即可
Python:
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
if not nums1 or not nums2:
return []
set1 = set(nums1)
set2 = set(nums2)
return set1&set2
复杂度分析:
时间复杂度:平均情况O(M+N),最坏情况下O(M*N),内置函数
空间复杂度:O(M+N),最坏情况下,元素都不相同
C++:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> set;
unordered_set<int> set1(nums1.begin(),nums1.end());
for(int num:nums2){
if(set1.count(num)==1) set.insert(num);
}
return vector<int>(set.begin(),set.end());
}
};
350. 两个数组的交集 II(简单)
与349的区别,输出结果中包含元素出现的次数,考虑dict
方法一:
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
if not nums1 or not nums2:
return []
dic1 = {
}
for num in nums1:
if num in dic1:
dic1[num] += 1
else:
dic1[num] = 1
res = []
for num in nums2:
if num in dic1:
if dic1[num]>0:
res.append(num)
dic1[num] -= 1
return res
用collection模块
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
from collections import Counter
if not nums1 or not nums2:
return []
dic1 = Counter(nums1)
res = []
for num in nums2:
if num in dic1:
if dic1[num]>0:
res.append(num)
dic1[num] -= 1
return res
方法二:
对应进阶的第一问,如果已排序,则采用双指针
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
if not nums1 or not nums2:
return []
nums1.sort()
nums2.sort()
p1 = 0
p2 = 0
res = []
while p1<len(nums1) and p2<len(nums2):
if nums1[p1]==nums2[p2]:
res.append(nums1[p1])
p1 += 1
p2 += 1
elif nums1[p1]<nums2[p2]:
p1 += 1
else:
p2 += 1
return res
复杂度分析:
时间复杂度:O(mlogm+nlogn),其中 m 和 n 分别是两个数组的长度。对两个数组进行排序的时间复杂度是 O(mlogm+nlogn),遍历两个数组的时间复杂度是 O(m+n),因此总时间复杂度是O(mlogm+nlogn)
空间复杂度:除了res数组,不需要额外的空间。
进阶
202. 快乐数(简单)
如果计算得到的数在之前曾经出现过,就会进入无限循环,因此用set。
class Solution:
def isHappy(self, n: int) -> bool:
def get_next(n):
res = 0
while n>0:
n1 = n%10
n = n//10
res += n1*n1
return res
set1 = set()
while n!=1 and n not in set1:
set1.add(n)
n = get_next(n)
return n==1
class Solution:
def wordPattern(self, pattern: str, str: str) -> bool:
str1 = str.split()
if len(pattern)!=len(str1):
return False
l = 0
dic = {
} #pattern[l]:str1[l]
while l<len(pattern):
if pattern[l] in dic:
if dic[pattern[l]] != str1[l]:
return False
else:
if str1[l] in dic.values(): ##如果pattern[l] not in dic, 但str1[l]出现过, 则为False
return False
dic[pattern[l]] = str1[l]
l += 1
return True
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
l = 0
dic = {
}
while l<len(s):
if s[l] in dic:
if dic[s[l]] != t[l]:
return False
else:
if t[l] in dic.values():
return False
dic[s[l]] = t[l]
l += 1
return True
class Solution:
def frequencySort(self, s: str) -> str:
from collections import Counter
s1 = Counter(s)
s2 = sorted(s1.items(),key=lambda item:item[1],reverse=True)
res = ''
for key,value in s2:
res += key*value
return res
Tips:
通过sorted的方法进行value排序,对字典排序后无法直接按照字典进行返回,返回的为列表元组:
#对value值由大到小排序
s = sorted(s_dict.items(), key=lambda item:item[1], reverse = True)
#对key由小到大排序
s = sorted(s_dict.items(), key=lambda item:item[0])
print(s) #[('e', 2), ('t', 1), ('r', 1)]
print(type(s)) #
输出为字符串的情况下,可以由字符串直接进行拼接:
#由key和value相乘进行拼接
's' * 5 + 'd'*2
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
from collections import Counter
l1 = len(s)
l2 = len(t)
if l1!=l2:
return False
if l1==0 and l2==0:
return True
dic = Counter(s)
for c in t:
if c not in dic:
return False
dic[c] -= 1
for key,value in dic.items():
if value!=0:
return False
return True
查找在算法题中是很常见的,但是怎么最大化查找的效率和写出bugfree的代码才是难的部分。一般查找方法有顺序查找、二分查找和双指针,推荐一开始可以直接用顺序查找,如果遇到TLE的情况再考虑剩下的两种,毕竟AC是最重要的。
一般二分查找的对象是有序或者由有序部分变化的(可能暂时理解不了,看例题即可),但还存在一种可以运用的地方是按值二分查找,之后会介绍。
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
l = 0
r = len(nums)-1
while l<=r:
mid = (r-l)//2 + l
if nums[mid]==target:
return mid
elif nums[mid]<target:
l = mid+1
else:
r = mid-1
return l
540. 有序数组中的单一元素(中等)
方法一:
依次遍历,判断
class Solution:
def singleNonDuplicate(self, nums: List[int]) -> int:
i = 0
while i<len(nums)-1:
if nums[i]!=nums[i+1]:
return nums[i]
i += 2
return nums[-1]
复杂度分析:
时间复杂度:最坏情况下,为O(N)
空间复杂度:常数
方法二:
mid=(r-l)//2+l
如果mid为偶数,说明mid之前的元素个数为偶数:
如果nums[mid]==nums[mid+1],则单一元素在右侧,即l=mid+1
如果nums[mid]!=nums[mid+1],则单一元素在左侧,即r=mid(nums[mid]可能是单一元素)
如果mid为奇数,说明mid之前的元素个数为奇数:
如果nums[mid]==nums[mid-1],则单一元素在右侧,即l=mid+1
如果nums[mid]!=nums[mid-1],则单一元素在左侧,即r=mid
注意边界条件,当 mid + 1 超出边界时,说明单一元素是数组的最后一个元素。
class Solution:
def singleNonDuplicate(self, nums: List[int]) -> int:
l = 0
r = len(nums)-1
while l<r:
mid = (r-l)//2+l
if mid+1>=len(nums):
return nums[mid]
if mid%2==0:
if nums[mid]==nums[mid+1]:
l=mid+1
else:
r=mid
else:
if nums[mid]==nums[mid-1]:
l=mid+1
else:
r=mid
return nums[l]
方法三: 巧用异或
如果mid是偶数,那么和1异或的话,那么得到的是mid+1,如果mid是奇数,得到的是mid-1
class Solution:
def singleNonDuplicate(self, nums: List[int]) -> int:
l = 0
r = len(nums)-1
while l<r:
mid = (r-l)//2+l
if mid+1>=len(nums):
return nums[mid]
if nums[mid]==nums[mid ^ 1]:
l=mid+1
else:
r=mid
return nums[l]
按值二分
410. 分割数组的最大值(困难)
求最大值最小,考虑二分法
注意:连续子数组
class Solution:
def splitArray(self, nums: List[int], m: int) -> int:
def helper(mid):
res=tmp=0
for num in nums:
if tmp+num<=mid:
tmp+=num
else:
res += 1
tmp = num
return res+1
l=max(nums)
r=sum(nums)
while l<r:
mid = (r+l)//2
if helper(mid)>m:
l = mid+1
else:
r = mid
return l
类似问题
875. 爱吃香蕉的珂珂(中等)
LCP 12. 小张刷题计划(中等)
1482. 制作 m 束花所需的最少天数(中等)
1011. 在 D 天内送达包裹的能力
参考liweiwei1419