目录
使用查找表的经典问题
1. 两数之和
15. 三数之和
18. 四数之和
16. 最接近的三数之和(不完全属于查找表)
454. 四数相加 II
49. 字母异位词分组
447. 回旋镖的数量
149. 直线上最多的点数
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
dic = dict()
for index,value in enumerate(nums):
sub = target - value
if sub in dic:
return [dic[sub],index]
else:
dic[value] = index
给定一个包含 n 个整数的数组 nums
,判断 nums
中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
优化方式见四数之和
class Solution:
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
先排序,从小到大选取第一个数,再在剩余区间内左右夹逼。
另外,排除一些不可能的情况可以提速很多。
{ … , a , … , b→ , … ,←c , …}
采用类似二分搜索的思想,用两个指针,这样时间复杂度是O(n^2)
"""
nums.sort()
res = []
for i in range(len(nums)-2):
#nums[i]与nums[i-1]的值相同时,在第一次nums[i-1]时就已经遍历完全为三和0的情况了
if i>0 and nums[i] == nums[i-1]:
continue
l = i+1
r = len(nums)-1
while l
法二:
freq = {}
for elem in nums:
freq[elem] = freq.get(elem, 0) + 1
if 0 in freq and freq[0] > 2:
res = [[0,0,0]]
else:
res = []
neg = sorted((filter(lambda x: x < 0, freq)))
nneg = sorted((filter(lambda x: x>= 0, freq)))
for elem1 in neg:
for elem2 in nneg:
target = -(elem1 + elem2)
if target in freq:
if target in (elem1, elem2) and freq[target] > 1: # 此target为elem1, elem2其中一个
res.append([elem1, target, elem2])
elif target < elem1 or target > elem2: # 此target不为elem1或elem2
res.append([elem1, target, elem2])
return res
给定一个包含 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]
]
在没优化之前是36%,优化之后83% 时间从1160ms到180ms
class Solution:
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
res = []
n = len(nums)
if n<4:
return res
nums.sort()
for i in range(n-3):
#优化1:最小的四数之和大于target或最大的四数之和小于target,break
if sum(nums[i:i+4]) > target or sum(nums[-4:]) < target:
break
#优化2:nums[i]与最大的后三个数之和小于target,continue for循环
if nums[i] + sum(nums[-3:])0 and nums[i]==nums[i-1]:
continue
target1 = target - nums[i]
for j in range(i+1,n-2):#3数之和的代码
#优化3:(当前i)最小的三数之和大于target1或最大的三数之和小于target1,break
if sum(nums[j:j+3])>target1 or sum(nums[-3:])i+1 and nums[j]==nums[j-1]:
continue
target2 = target1 - nums[j]
l = j+1
r = len(nums)-1
while l
给定一个包括 n 个整数的数组 nums
和 一个目标值 target
。找出 nums
中的三个整数,使得它们的和与 target
最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
优化后:100%,优化前40%
class Solution:
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
nums.sort()
closest_sum = nums[0]+nums[1]+nums[2]#初始化
min_diff = abs(closest_sum-target)
for i in range(len(nums)-2):
#优化1:
if i>0 and nums[i]==nums[i-1]:
continue
l = i+1
r = len(nums)-1
#优化2:当前i与最大两个数之和小于target且差的绝对值小于之前的min_diff(减少了循环次数)
cur_max_sum = nums[i]+nums[r-1]+nums[r]
if cur_max_sum
给定四个包含整数的数组列表 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
解释:
两个元组如下:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
暴力解法O(n^4) 执行6亿次; 将D放入查找表O(n^3)执行1亿次;
class Solution:
def fourSumCount(self, A, B, C, D):
"""
:type A: List[int]
:type B: List[int]
:type C: List[int]
:type D: List[int]
:rtype: int
"""
#时间复杂度O(n^2),空间复杂度O(n^2)
#from collections import defaultdict
#dic = defaultdict(int)
dic = {}
for c_num in C:
for d_num in D:
dic[c_num+d_num] = dic.get(c_num+d_num,0)+1
res = 0
for a_num in A:
for b_num in B:
s = 0-a_num-b_num
if s in dic.keys():
res += dic[s]
return res
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
,
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
说明:
class Solution:
def groupAnagrams(self, strs):
"""
:type strs: List[str]
:rtype: List[List[str]]
先将字符串数组中的每一个字符串列表化然后排序,变成字符串,通过这种变换字母异位词变成相同的,
生成的字符串作为dic的key,value为k,k为字符串列表的索引,这样将字母异位词组合在一个列表中
"""
res = []
dic = {}
k=0
for i in range(len(strs)):
onestrlist = (list(strs[i])).sort()
onestrlist="".join(onestrlist)
#onestrlist = sortstr(onestr)
if onestrlist in dic.keys():
res[dic[onestrlist]].append(strs[i])
else:
dic[onestrlist]=k
res.append([strs[i]])
k+=1
return res
法2:
dict={}
for each_str in strs:
new_str=''.join(sorted(each_str))
if new_str in dict:
dict[new_str].append(each_str)
else:
dict[new_str]=[each_str]
return list(dict.values())
给定平面上 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]]
class Solution:
def numberOfBoomerangs(self, points):
"""
:type points: List[List[int]]
:rtype: int
"""
#时间复杂度O(n^2),空间O(n)
res = 0
for x in points:
dic = {} #key=距离,value=频率
for y in points:
i = x[0] - y[0]
j = x[1] - y[1]
#由于是n对不同的点,x,y是同一个点时,距离=0的值频率只有xy是同一个点的时候
dis = i*i+j*j
dic[dis] = dic.get(dis,0)+1
for item in dic.values():
res += item * (item - 1) #value=1的值不影响res
return res
给定一个二维平面,平面上有 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
测试用例中有 [[0,0],[94911151,94911150],[94911152,94911151]],因此要用longdouble
# Definition for a point.
# class Point:
# def __init__(self, a=0, b=0):
# self.x = a
# self.y = b
class Solution:
def maxPoints(self, points):
"""
:type points: List[Point]
:rtype: int
"""
import numpy as np
length = len(points)
if length < 3:
return length
res = 0
for i in range(length):
dic={'in_y':0} #key是斜率,value是点的数量
same = 1
for j in range(i+1,length):
x = points[i].x-points[j].x
y = points[i].y-points[j].y
if y != 0 and x == 0:#都为0是同一个点,y!=0 x==0平行于y轴
dic['in_y'] += 1
elif x!=0:
k = np.longdouble(1) * y / x
dic[k] = dic.get(k,0) + 1
else:
same +=1 #列表中有与i重复的点,因此i与其他点构成的直线,都要加上same的个数
res = max(max(dic.values())+same,res)
return res