zhe两数之和 , 两次遍历, 第一次 生成对应位置值的差值字典, 第二次判断差值字典存在,则找到一组答案. leetcode 第一题. On
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
tmp = {}
for i in range(len(nums)):
diff = target-nums[i]
tmp[diff] = i
for i in range(len(nums)):
if nums[i] in tmp and i != tmp[nums[i]]:
return [i, tmp[nums[i]]]
leetcode 167 输入有序数组的两数之和; 区别是输入有序非递减,输出要求有序; 仍可以用两数和的思路, 两次遍历. 双指针也可以用.
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
tmp = {}
for i in range(len(numbers)):
# tmp[target-numbers[i]] = i # 存储的是下标, 非递减时下标一定是不同的,值可能相同会覆盖, 所以key要换成下标啊, value 是对应的差值
# tmp[i] = target-numbers[i] #key 为下标, value 为差值时 ,下次循环获取对应的key 有点麻烦, 改造一下数据结构, hash 碰撞处理
if target-numbers[i] in tmp: # key 仍为差值, value 为下标列表
tmp[target-numbers[i]].append(i)
else:
tmp[target-numbers[i]] = [i]
for i in range(len(numbers)):
if numbers[i] in tmp:
for j in range(len(tmp.get(numbers[i]))):
if tmp[numbers[i]][j] != i: # 找到一组
# return [tmp[numbers[i]][j]+1,i+1] # 返回的下标必须有序 满足 index1<index2
ret = [tmp[numbers[i]][j]+1,i+1]
ret.reverse() # 本身是升序所以, i < target-i
return ret
653. 两数之和 IV - 输入二叉搜索树https://leetcode.cn/problems/two-sum-iv-input-is-a-bst/
判断in self.s 时,如果in 则必然不是本节点, 所以不需要前面!=自己下标
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def __init__(self):
self.s = set()
def findTarget(self, root: Optional[TreeNode], k: int) -> bool:
if root is None :
return False
if k-root.val in self.s:
return True
self.s.add(root.val)
return self.findTarget(root.left,k) or self.findTarget(root.right, k)
查找两棵二叉搜索树之和https://leetcode.cn/problems/two-sum-bsts/
3sum 用固定+ 双指针; 然后做超时优化. 时间复杂o(nlogn) + o(n2)
class Solution:
def __init__(self):
self.l = list() # set 自动去重
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
# 超时
# 固定一个数, 剩下的双指针
for i in range(len(nums)-2): # 第一次优化有边界 n-2 ,至少需要三个元素
# 第三次优化,跳过重复的i
if i>0 and nums[i] == nums[i-1]:
continue
left = i+1
right = len(nums)-1
while(left<right):
if nums[left] + nums[right] == -nums[i]:
# 第二次优化, 跳过重复元素
self.l.append([nums[i],nums[left],nums[right]])
while(left<right and nums[left] == nums[left+1]):
left+=1
while(left<right and nums[right] == nums[right-1]):
right-=1
# 第四次优化,这里有问题,找到一个结果后, left ,right 继续变
left+=1
right-=1
elif nums[left] + nums[right] > -nums[i]:
right-=1
else :
left+=1
# return self.s # 返回结果要求是list 用set 不合适
return self.l
继续用3sum的思想搞ac了, 增加剪枝优化
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
# 套用三数和
nums.sort()
n = len(nums)
ret = []
if n<4:
return ret
for i in range(n-3):
if i >0 and nums[i] == nums[i-1]:
continue
# 优化下增加剪枝, 最小的四个和比target大 后面的不用看了,使用索引千面判断下
if nums[i]+nums[i+1] + nums[i+2] +nums[i+3] > target:
break
# 再优化剪枝, 当前+ 最大的数 比target小, 跳过当前
if nums[i] + nums[n-1] + nums[n-2] + nums[n-3] < target:
continue
for j in range(i+1,n-2):
if j>i+1 and nums[j] == nums[j-1]:
continue
# 优化下增加剪枝, 最小的四个和比target大 后面的不用看了,使用索引千面判断下
if nums[i]+nums[j] + nums[j+1] +nums[j+2] > target:
break
if nums[i] + nums[j] + nums[n-1] + nums[n-2] < target:
continue
l, r= j+1, n-1
while l<r:
if target == nums[i] + nums[j] + nums[l] + nums[r]:
# 找到一个结果
ret.append([nums[i],nums[j],nums[l],nums[r]])
# 找到后跳过重复
while l<r and nums[l] == nums[l+1]:
l+=1
while l<r and nums[r] == nums[r-1]:
r-=1
l+=1
r-=1
elif target > nums[i] + nums[j] + nums[l] + nums[r]:
l+=1
else:
r-=1
return ret
最接近的三数之和https://leetcode.cn/problems/3sum-closest/
接着用双指针, 跳指针的时候 先跳过重复的再移动, 若先移动再跳出问题难debug.
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
# 排序数组 nlogn
nums.sort()
# 初始化结果
ret = nums[0]+nums[1]+nums[2]
# 初始化最接近的值
# min_diff = abs(ret-target)
n = len(nums)
# 遍历nums
for i in range(n-2): # 三数
first = nums[i]
if i>0 and nums[i] == nums[i-1]: #去重
continue
left,right =i+1,n-1
while left < right: # 用三数和双指针思想
total = nums[i] + nums[left] + nums[right]
if (total == target): # 0最接近直接返回
return total
# total 与 target 更接近更新 ret, 更新最小diff
if abs(total-target) < abs(ret-target):
ret = total
# min_diff = abs(total-target)
# 固定步长导致漏掉一些组合, 按需移动
if total > target:
# 移动指针时跳过重复值
while left<right and nums[right] == nums[right-1]:
right-=1
# 跳过后再移动
right-=1
else:
while left<right and nums[left]==nums[left+1]:
left+=1
left +=1
return ret
等差三元组的数目https://leetcode.cn/problems/number-of-arithmetic-triplets/
BF就行了,没有别的必要
class Solution:
def arithmeticTriplets(self, nums: List[int], diff: int) -> int:
nums = set(nums)
# 严格递增, n >=3 , 且diff 指定
n = len(nums)
ret =0
# 从第一个确定位置开始找diff
for i in nums: # 三元组
# 假设使用双指针, left , right ; 三元组的 right - left 一定等于2diff
# 对每个位置 nums[i] + diff 存在 且 nums[i] + 2diff 存在 则找到一组答案, 剩下的肯定不是
if (i + diff) in nums and (i+2*diff) in nums:
ret+=1
return ret
元素和最小的山形三元组 Ihttps://leetcode.cn/problems/minimum-sum-of-mountain-triplets-i/
可以BF, for i xx : for j xx : for k xx : if i<j and j>k : do :
前缀和思想两次遍历挺合适的.
class Solution:
def minimumSum(self, nums: List[int]) -> int:
# n>=3 且 j 左右一定有小于i ,k 否则-1, 题目任意nums[i]<50
ret = 200
# 两次遍历,前缀数组思想
n = len(nums)
left, right = [0 for i in range(n)], [0] * n
mni = 1000 # 随便定义个,
mxi = 1000
left[0] = 1000
right[n-1] = 1000
for i in range(1,n): # 从1开始, 0 左边没有元素
left[i] = mni = min(nums[i-1], mni)
for i in range(n-2,0,-1):
right[i] = mxi = min(nums[i+1], mxi)
for i in range(n):
if nums[i]>left[i] and nums[i] > right[i]:
ret = min(ret, nums[i]+left[i]+right[i])
return -1 if ret == 200 else ret
元素和最小的山形三元组 IIhttps://leetcode.cn/problems/minimum-sum-of-mountain-triplets-ii/
跟山形三元组1 一样解哦, 只是数量大了不好用BF
class Solution:
def minimumSum(self, nums: List[int]) -> int:
# 与山型三元组1 相同用前缀和思想搞下
n = len(nums)
left, right = [0]*n,[0]*n
left[0] = float('inf')
right[n-1] = float('inf')
ret = float('inf')
for i in range(1,n):
left[i] = min(nums[i-1], left[i-1])
for i in range(n-2,-1,-1): #到0吧
right[i] = min(nums[i+1], right[i+1])
for i in range(n):
if(nums[i] > left[i] and nums[i]>right[i]):
ret = min(ret, nums[i]+left[i]+right[i])
return -1 if ret == float('inf') else ret
四数相加 IIhttps://leetcode.cn/problems/4sum-ii/
用python的counter 结构方便实现, 其他语言自适配
from collections import Counter
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
ret = 0
ab = [a + b for a in nums1 for b in nums2]
ab = Counter(ab)
# print(ab)
cd = [-c-d for c in nums3 for d in nums4]
cd = Counter(cd)
# print(cd)
for f in cd :
if f in ab:
ret += ab[f] * cd[f]
return ret
统计特殊四元组https://leetcode.cn/problems/count-special-quadruplets/
数据量不大用的BF ac , 量大肯定不行, 需要优化 感觉还是 四数相加 a+b =d-c 的思路延续;
class Solution:
def countQuadruplets(self, nums: List[int]) -> int:
# 数据量不大BF
ret =0
n = len(nums)
# a + b = d - c ? 四数相加思路?, 四数和的双指针不太行
# a < b < c < d
for i in range(n-3): # 4个数
for j in range(i+1, n-2): #3个数
# 固定了i+j ,剩下的 c d
for c in range(j+1, n-1):
for d in range(c+1,n):
if nums[i] + nums[j] + nums[c] == nums[d]:
ret+=1
return ret
ksum 用回溯模板弄吧
from typing import List
def backtrack(nums: List[int], target: int, k: int, start: int, current_sum: int, path: List[int], result: List[List[int]]):
if len(path) == k:
if current_sum == target:
result.append(path.copy())
return
for i in range(start, len(nums)):
if i > start and nums[i] == nums[i - 1]:
continue # 跳过重复的元素
if current_sum + nums[i] > target:
break # 剪枝:如果当前和已经超过目标,提前终止
path.append(nums[i])
backtrack(nums, target, k, i + 1, current_sum + nums[i], path, result)
path.pop()
def kSum(nums: List[int], target: int, k: int) -> List[List[int]]:
nums.sort() # 首先对数组进行排序
result = []
path = []
backtrack(nums, target, k, 0, 0, path, result)
return result