leetcode 1 两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
力扣第一题,就像词汇书的abandon,无脑解法就是套两个循环遍历,这里使用复杂度更低的写法。
【思路】
将数组排序,并且标记原始数组中的索引,可以一遍遍历出结果
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
# 记录索引
nums=list(enumerate(nums))
# 根据元素大小排序
nums=sorted(nums, key=lambda x: x[1])
i,j=0,len(nums)-1 # 记录坐标
while nums[i][1]+nums[j][1]!=target:
if nums[i][1]+nums[j][1]>target:
j-=1
elif nums[i][1]+nums[j][1]<target:
i+=1
return sorted([nums[i][0],nums[j][0]])
leetcode 15 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
【思路】
使用指针对撞,有三个数字需要记录三个坐标,最外层遍历第一个坐标 i
,将数列排序后即从最小的一项开始遍历,若该项>0,则不存在三数之和为0,中间位置从 i+1
开始遍历,末尾位置从 len(n)
向前遍历,该两点形成对撞式的遍历。
防止数列中的重复元素被重复计算,若下一个点等于当前点,则跳过下一个点。
代码实现:
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort() # 将数列排序
res=[]
for i in range(len(nums)-2):
if nums[i]>0: break # 第一个元素就大于0 则不存在三数之和为0
if i>0 and nums[i]==nums[i-1]: continue # 跳过重复元素
l,r=i+1,len(nums)-1 # 后两个元素遍历初始位置
while l<r:
s=nums[i]+nums[l]+nums[r]
if s==0:
res.append([nums[i],nums[l],nums[r]])
l+=1
r-=1
while l<r and nums[r]==nums[r+1]: # 跳过重复元素 且确保l<r
r-=1
while l<r and nums[l]==nums[l-1]:
l+=1
if s>0:r-=1
if s<0:l+=1
return res
【处理技巧】
- 采用for + while的形式来处理三索引;
- 当数组不是有序时需要注意,有序的特点在哪里,有序就可以用哪些方法解决?无序的话不便在哪里?
- 对撞指针套路:
# 对撞指针套路
l,r = 0, len(nums)-1
while l < r:
if nums[l] + nums[r] == target:
return nums[l],nums[r]
elif nums[l] + nums[r] < target:
l += 1
else:
r -= 1
- 处理重复值的套路:先转换为有序数组,再循环判断其与上一次值是否重复:
# 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 454 四数之和2
给定四个包含整数的数组列表 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
【思路】
纯暴力:O(n^4)
小暴力:字典储存一个数组,key为数组中的元素,value为该元素出现的次数,再遍历剩余数组,O(n^3)
小小暴力:字典储存两个数组的相加和,key为两数之和,value为该结果的次数,在遍历剩余数组,O(n^2)
class Solution:
def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:
from collections import Counter
record=Counter(a+b for a in A for b in B) # 储存两数组之和
return sum([record.get(-d-c,0) for c in C for d in D]) # 遍历剩余两数组
leetcode 149 一条线上的点
给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。
【思路】
y
=
k
x
+
b
y=kx+b
y=kx+b 确定一条直线,获得两点之间的斜率和截距就可以得到一条线,计算每两个点练成的线,用字典储存,key为斜率和截距,值为这条线上的点
class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
# 将点按照从小到大排序 方便计算斜率
points.sort(key=lambda x: x[0])
points.sort(key=lambda x: x[1])
if len(points)<3 : return len(points)
'''
由于当数值特别大时,精度计算会出问题,故使用最简分数形式表达,即分子分母除以最大公约数
'''
def gcd(x,y): # 最大公约数函数
if y==0:
return x
else:
return gcd(y,x%y)
def linear(a,b): # 计算斜率和截距
if a[0]==b[0]: return a[0]
y,x=a[1]-b[1],a[0]-b[0]
k=y/x
b=a[1]-k*a[0]
g=gcd(x,y)
if g!=0:
k='{},{}'.format(y//g,x//g)
else:
k='{},{}'.format(y,x)
return ','.join([str(k),str(b)])
from collections import defaultdict
dic=defaultdict(list)
for i in range(len(points)-1):
for j in range(i+1,len(points)):
if points[i] not in dic[linear(points[j],points[i])]:
dic[linear(points[j],points[i])].append(points[i])
if points[j] not in dic[linear(points[j],points[i])]:
dic[linear(points[j],points[i])].append(points[j])
# 由于存在重复点,对点进行计数
return max([sum([points.count(i) for i in v]) for v in dic.values()])