1.两数之和
给定一个整数数组nums和一个目标值target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定
nums = [2, 7, 11, 15], target = 9
因为
nums[0] + nums[1] = 2 + 7 = 9
所以返回[0, 1]
解法一:
将数组排序,之后设置两个高低指针,从两端遍历,若所指数之和小于target,则低指针加一,若大于则高指针减一,等于则返回。
class Solution:
def twoSum(self, nums: list, target: int) -> list:
nums.sort()
i,j=0,len(nums)-1
while i<j:
if nums[i]+nums[j]==target:
return [i,j]
elif nums[i]+nums[j]<target:
i+=1
else:
j-=1
return []
a=Solution()
print(a.twoSum([1,1,2,3,3,4,4,8],16))
解法二:
暴力求解,两层循环:
class Solution:
def twoSum(self, nums: list, target: int) -> list:
n = len(nums)
for x in range(n):
for y in range(x + 1, n): #x之前的已经试过了,所以从x+1开始
if nums[x] + nums[y] == target:
return x, y
a=Solution()
print(a.twoSum([1,1,2,2,3,3,4,4,8,8],3))
解法三:使用字典映射,键为数组元素,值为数组元素的下标:
class Solution:
def twoSum(self, nums: list, target: int) -> list:
d={
}
for i in range(len(nums)):
if target-nums[i] in d:
return [i,d[target-nums[i]]]
else:
d[nums[i]]=i
return []
a=Solution()
print(a.twoSum([1,1,3,2,2],6))
- 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
解法一:三数之和为0,则至少有一个数必然小于等于0,根据此性质,将数组排序,则第一个数应存在于小于等于0的范围,第二个数和第三个数在第一个数之后的范围里寻找,即化为了两数之和的问题,使用高低指针法解决即可:
class Solution:
def threeSum(self, nums: list) -> list:
nums.sort()
li=[]
for i in range(len(nums)):
if nums[i]>0:
return li
if i>0 and nums[i]==nums[i-1]:
continue
target=-nums[i]
j,k=i+1,len(nums)-1
while j<k:
if nums[j] + nums[k] == target:
a=[nums[i],nums[j],nums[k]]
a.sort()
if a not in li:
li.append(a)
k-=1
elif nums[j] + nums[k] < target:
j += 1
else:
k -= 1
return li
a=Solution()
print(a.threeSum([0,0,0]))
- 最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
提示:
3 <= nums.length <= 10^3
-10^3 <= nums[i] <= 10^3
-10^4 <= target <= 10^4
解法一:
此题将三数之和为0改为三数之和最接近某数,令后两数之和等于target减去第一个数即可回到三数之和的套路,定义一个ans来记录最接近target的值,
然后依然使用高低指针法查找,每次产生的的三数之和都要判断看是否需要更新,注意判断更新的条件写法以及高低指针移动时跳过相同值:
class Solution:
def threeSumClosest(self, nums: list,target:int):
nums.sort()
n = len(nums)
ans = 10**10
for i in range(n):
if i>0 and nums[i]==nums[i-1]:
continue
j,k=i+1,n-1
while j<k:
s=nums[i] + nums[j] + nums[k]
if abs(s - target) < abs(ans - target):
ans = s
if s==target:
return target
elif s<target:
j1= j+1
while j1<k and nums[j1]==nums[j]:
j1+=1
j=j1
else:
k1 = k - 1
while k1 >j and nums[k1] == nums[k]:
k1 += 1
k = k1
return ans
a=Solution()
print(a.threeSumClosest([-1,2,1,-4],1))
#2
- 四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
解法一:
此题需要多加一层循环,选定第一个数后,第二个数从第一个数的后一个数开始,就转化为了三数之和问题,注意每一层循环里最开始跳过重复值的细节
class Solution:
def threeSumClosest(self, nums: list,target:int):
nums.sort()
li = []
n=len(nums)
for x in range(n):
if x > 0 and nums[x] == nums[x - 1]:
continue
for i in range(x+1,n):
if i > x+1 and nums[i] == nums[i - 1]: #多个相同值的第一个是不可以跳过的
continue
j, k = i + 1, len(nums) - 1
while j < k:
s=nums[x] +nums[i] +nums[j] + nums[k]
if s == target:
a = [nums[x],nums[i], nums[j], nums[k]]
if a not in li:
li.append(a)
while j < k and nums[j] == nums[j + 1]:
j += 1
while j < k and nums[k] == nums[k - 1]:
k -= 1
k -= 1
j += 1
elif s < target:
j += 1
else:
k -= 1
return li
a=Solution()
print(a.threeSumClosest([1, 0, -1, 0, -2, 2],0))
#[[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]]
- 字母异位词分组
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]
输出:
[
[“ate”,“eat”,“tea”],
[“nat”,“tan”],
[“bat”]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
方法一:排序法
使用collections.defaultdict来初始化一个字典,然后将每一个字符串都排序然后转化成元组,作为键写入字典,字典的值最开始是一些空列表,用来记录字符组成与键相同的字符串:
import collections
class Solution:
def groupAnagrams(self, strs:list):
dic1 = collections.defaultdict(list)
for i in strs:
a=tuple(sorted(i))
dic1[a].append(i)
return dic1.values()
a=Solution()
print(a.groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"]))
方法二:计数法
还是创建一个字典,键为一个记录字母出现次数的元组,值还是记录字符串的列表:
import collections
class Solution:
def groupAnagrams(self, strs:list):
dic1 = collections.defaultdict(list)
for s in strs:
count = [0] * 26
for c in s:
count[ord(c) - ord('a')] += 1
dic1[tuple(count)].append(s)
return dic1.values()
a=Solution()
print(a.groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"]))
- 直线上最多的点数
给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。
示例 1:
输入: [[1,1],[2,2],[3,3]]
输出: 3
解释:
^
|
| o
| o
| o
±------------>
0 1 2 3 4
示例 2:
输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出: 4
解释:
^
|
| o
| o o
| o
| o o
±------------------>
0 1 2 3 4 5 6
解法一:使用求最大公约数的方法求斜率,化简为int/int最简形式,这样不存在精度问题,创建一个字典,以化简后的斜率为键,统计经过一个点的所有直线中哪个直线上点最多,依次遍历所有点,即可找出直线上最多的点:
class Solution:
def maxPoints(self, points: list) -> int:
from collections import Counter, defaultdict
# 所有点统计
points_dict = Counter(tuple(point) for point in points)
print(points_dict)
# 把唯一点列举出来
not_repeat_points = list(points_dict.keys())
n = len(not_repeat_points)
if n == 1: return points_dict[not_repeat_points[0]]
res = 0
# 求最大公约数
def gcd(x, y):
if y == 0:
return x
else:
return gcd(y, x % y)
for i in range(n - 1):
# 点1
x1, y1 = not_repeat_points[i][0], not_repeat_points[i][1]
# 斜率
slope = defaultdict(int)
for j in range(i + 1, n):
# 点2
x2, y2 = not_repeat_points[j][0], not_repeat_points[j][1]
dy, dx = y2 - y1, x2 - x1
# 方式一 利用公约数
g = gcd(dy, dx)
if g != 0:
dy //= g
dx //= g
slope["{}/{}".format(dy, dx)] += points_dict[not_repeat_points[j]]
res = max(res, max(slope.values()) + points_dict[not_repeat_points[i]])
return res
a=Solution()
print(a.maxPoints([[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]))
存在重复元素
给定一个整数数组和一个整数k,判断数组中是否存在两个不同的索引i和j,使得nums[i] = nums[j],并且i和j的差的绝对值至多为k。
示例1:
输入: nums = [1, 2, 3, 1], k = 3
输出: true
示例2:
输入: nums = [1, 0, 1, 1], k = 1
输出: true
示例
3:输入: nums = [1, 2, 3, 1, 2, 3], k = 2
输出: false
解法一:暴力解法,用一个指针i遍历数组,检查在i+1到i+k+1的范围内所有值看是否与i相同:
class Solution:
def containsNearbyDuplicate(self, nums: list, k: int) -> bool:
for i in range(len(nums)):
for j in range(i+1,min(i+k+1,len(nums))):
if nums[i]==nums[j]:
return True
return False
a=Solution()
print(a.containsNearbyDuplicate([1,2,3,1],3))#True
print(a.containsNearbyDuplicate([1,0,1,1],1))#True
print(a.containsNearbyDuplicate([1,2,3,1,2,3],2))#False
解法二:建立一个字典,遍历nums中的元素,如果nums[i]在字典中并且下标与i的距离小于等于k,则返回true:
class Solution:
def containsNearbyDuplicate(self, nums: list, k: int) -> bool:
dict = {
}
for i in range(len(nums)):
if nums[i] in dict and i - dict[nums[i]] <= k:
return True
dict[nums[i]] = i
return False
存在重复元素
在整数数组nums中,是否存在两个下标i和j,使得nums[i]和nums[j]的差的绝对值小于等于t ,且满足i和j的差的绝对值也小于等于ķ 。
如果存在则返回true,不存在返回false。
示例1:
输入: nums = [1, 2, 3, 1], k = 3, t = 0
输出: true
示例2:
输入: nums = [1, 0, 1, 1], k = 1, t = 2
输出: true
示例3:
输入: nums = [1, 5, 9, 1, 5, 9], k = 2, t = 3
输出: false
解法一:遍历I,在I后的k个数里寻找符合条件的数
class Solution:
def containsNearbyAlmostDuplicate(self, nums: list, k: int,t: int) -> bool:
n=len(nums)
for i in range(n):
for j in range(i+1,min(i+k+1,n)):
if abs(nums[i]-nums[j])<=t:
return True
return False
a=Solution()
print(a.containsNearbyAlmostDuplicate([1,2,3,1],3,0))
print(a.containsNearbyAlmostDuplicate([1,0,1,1],1,2))
print(a.containsNearbyAlmostDuplicate([1,5,9,1,5,9],2,3))
方法二:
class Solution:
def containsNearbyAlmostDuplicate(self, nums: list, k: int,t: int) -> bool:
if t < 0 or not k or not nums:
return False
if k == 1:
for i in range(len(nums) - 1):
if abs(nums[i] - nums[i + 1]) <= t:
return True
return False
if not t:
dct = {
}
for inx, i in enumerate(nums):
if i in dct:
if inx - dct[i] <= k:
return True
dct[i] = inx
return False
lst = []
i = nums[0]
lst.append(sum([(i - j, i + j) for j in range(t + 1)], ()))
for i in nums[1:]:
if i in set(sum(lst, ())):
return True
lst.append(sum([(i - j, i + j) for j in range(t + 1)], ()))
lst = lst[-k:]
return False
a=Solution()
print(a.containsNearbyAlmostDuplicate([1,2,3,1],3,0))
print(a.containsNearbyAlmostDuplicate([1,0,1,1],1,2))
print(a.containsNearbyAlmostDuplicate([1,5,9,1,5,9],2,3))
- 回旋镖的数量
给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺序)。
找到所有回旋镖的数量。你可以假设 n 最大为 500,所有点的坐标在闭区间 [-10000, 10000] 中。
示例:
输入:
[[0,0],[1,0],[2,0]]
输出:
2
解释:
两个回旋镖为 [[1,0],[0,0],[2,0]] 和 [[1,0],[2,0],[0,0]]
解法一:每次固定一个点,使用哈希表存储其他点到这个点的距离,如果存在记录次数,回旋镖的数量应为次数*(次数-1):
class Solution:
def numberOfBoomerangs(self, points: list[list]) -> int:
res=0
for i in points:
dicts={
}
for j in points:
if i==j:
continue
dicts[(i[0]-j[0])**2+(i[1]-j[1])**2]=dicts.get((i[0]-j[0])**2+(i[1]-j[1])**2,0)+1
for i in dicts.values():
res+=i*(i-1)
return res
- 四数相加 II
给定四个包含整数的数组列表 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 。
例如:
输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
输出:
2
解释:
两个元组如下: - (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
- (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
解法一:
初始化计数器 dic,dic 记录数组 A 和 B 元素的和,及其次数
遍历数组 C 和 D,累加满足四数相加和为 0 的个数:
class Solution:
def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:
dic = collections.Counter()
ans = 0
for a in A:
for b in B:
dic[a+b] += 1
for c in C:
for d in D:
ans += dic[-c-d]
return ans