指针
6.三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
题解
该题需要先从小到大排序,后需要建立三个指针,从头到尾完成遍历,第一层遍历决定第一个元素(first),第二层遍历决定第二个元素(second = first + 1)跟第三个元素(third,初始化为最大的元素)。其中对于确认了first后,就要对second和third进行首尾遍历。原则为:三者相加,大于target则左移third,小于则右移second。另外需要注意,不能输出相同的元素,又因为该数组一开始就经过排序,则可以跳过相等的两个元素。
class Solution:
def threeSum(self, nums) -> list[list[int]]:
res = []
# 先进行排序
nums.sort()
for first in range(len(nums)):
# 第一层遍历
if first > 0 and nums[first] == nums[first - 1]:
# 相邻的两个元素相等,则跳过
continue
# 第三个元素初始化为排列后的最后一个,即数组最大值
third = len(nums) - 1
# target = -nums[first]
target = -nums[first]
# 第二个元素初始化为第一个元素的后一个元素
for second in range(first + 1, len(nums) - 1):
# 对数组进行《首尾收缩遍历》
if second > first + 1 and nums[second] == nums[second - 1]:
# 相邻的两个元素相等,则跳过
continue
while second < third and nums[second] + nums[third] > target:
# 第二第三元素相加大于target,则向头部收缩
third -= 1
if second == third:
# 相等了,则直接跳出内层循环
break
elif nums[second] + nums[third] == target:
res.append([nums[first], nums[second], nums[third]])
return res
7.最接近的两数和 - 字节-上海
给出一个数组和一个目标值,找出两个和最接近目标值的子项
题解
数组从大到小排序,双指针首尾收缩遍历,当前两者相加大于目标则收缩尾部,小于则收缩头部,用res记录最接近目标值的值
def testNear(arrNear, targetNear) -> int:
# 排序
arrNear.sort()
left = 0
right = len(arrNear) - 1
res = float('inf') # 初始化为正无穷大,用于比较寻找最接近的值
while left < right:
# 《首尾收缩遍历》
temp = arrNear[left] + arrNear[right]
if temp == targetNear:
# 如果找到了精确匹配,返回这个和(但根据原逻辑,这里实际上不会执行,因为res未更新)
# 但由于题目要求返回最接近的值,且这里逻辑上存在问题,我们假设仅更新res
res = temp
break # 可选择跳出循环,如果找到精确解则不再继续
elif temp > targetNear:
# 尾部收缩
right -= 1
else:
# 头部收缩
left += 1
# 更新最接近的值
if abs(targetNear - temp) < abs(targetNear - res):
res = temp
return res
# 测试用例
arrNear = [24, 69, 14, 37]
targetNear = 60
nearP = testNear(arrNear, targetNear)
print({"nearP": nearP}) # 输出: {'nearP': 61} 或其他最接近的值,取决于数组和目标值
8 接雨水 - 腾讯cdg
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
题解
一:动态规划
用dp数组记录下标i及其左(leftMax )右(rightMax)边的所有柱子的最大高度。dp数组初始化,leftMax[0] = height[0];和rightMax[len - 1] = height[len - 1];。dp数组遍历过程中,左右侧的值与当值的高度进行对比,更新dp数组
leftMax[i] = Math.max(leftMax[i - 1], height[i]);
rightMax[j] = Math.max(rightMax[j + 1], height[j]);
左右侧最高的两条柱子中,矮的那条减自身高度,即为当前柱子能接的水
class Solution:
def trap(self, height: List[int]) -> int:
# 动态规划
length = len(height)
if length == 0:
return 0
# 下标i及其左边的所有柱子的最大高度
left_max: list[int] = [0] * length
# 下标i及其右边的所有柱子的最大高度
right_max: list[int] = [0] * length
# 初始化
left_max[0] = height[0]
right_max[length - 1] = height[length - 1]
# 更新左侧最大高度
for i in range(1, length):
left_max[i] = max(left_max[i - 1], height[i])
# 更新右侧最大高度
for j in range(length - 2, -1, -1):
right_max[j] = max(right_max[j + 1], height[j])
ans: int = 0
# 计算每个位置能接的水量
for k in range(length):
# 左右侧最高的两条柱子中,矮的那条减自身高度,即为当前柱子能接的水
ans += min(left_max[k], right_max[k]) - height[k]
return ans
# 测试用例
# e = trap_dp([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 'a'])
# print({"e": e}) # 输出: {'e': 6}
二、双指针
头尾双指针收缩遍历,详细见代码解
class Solution:
def trap(self, height: List[int]) -> int:
# 双指针
len_height = len(height)
left = 0
right = len_height - 1
# 初始化左右最大值
left_max = height[0]
right_max = height[len_height - 1]
ans = 0
while left < right:
# 左侧最大值与左指针当前值相比,更新最大值
left_max = max(left_max, height[left])
# 右侧最大值与右指针当前值相比,更新最大值
right_max = max(right_max, height[right])
# 移动较矮的指针(因为矮的指针决定能存多少水--木桶原理),累加可以积累的雨水
if height[left] < height[right]:
ans += left_max - height[left]
left += 1
else:
ans += right_max - height[right]
right -= 1
return ans
# 测试用例
# f = trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])
# print({"f": f}) # 输出: {'f': 6}
9 无重复字符串的最长子串
给定一个字符串s ,请你找出其中不含有重复字符的 最长
子串的长度。
题解
双指针搭配set, 用set去重,左指针移动, set移除元素,右指针移动,且右指针元素不存在于set中,则加入set,最后根据左右指针位置更新最大长度
def length_of_longest_substring(s: str) -> int:
# 左右指针
# 用set去重
set_new = set()
left = 0 # 初始化左指针
right = -1 # 右指针从-1开始,方便处理第一个字符
ans = 0
while right + 1 < len(s):
# 尝试移动右指针
if s[right + 1] not in set_new:
set_new.add(s[right + 1])
right += 1
# 更新最长无重复字符子串的长度
ans = max(ans, right - left + 1)
else:
# 当右指针指向的字符已存在于set中时,移动左指针
set_new.remove(s[left])
left += 1
return ans
# 测试用例
g = length_of_longest_substring('abcabcbb')
print({"g": g}) # 输出: {'g': 3}
10 找到字符串中所有字母异位词
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
题解
使用《位置数组》记录当前值在英文字母中的顺序。初始化遍历先把p字符串和s下标0到pLen - 1的子串遍历完。如这个时候如果位置数组相等,则把下标0推进数组。后将s剩下的数据遍历完成。
def find_anagrams(s: str, p: str) -> list:
s_len = len(s)
p_len = len(p)
if s_len < p_len:
# s长度小于p长度,s中不存在p的异位词子串
return []
ans = []
# 初始化位置数组(字符计数)
s_count = [0] * 26
p_count = [0] * 26
# 初始化p和s的前p_len个字符的计数
for i in range(p_len):
s_count[ord(s[i]) - ord('a')] += 1
p_count[ord(p[i]) - ord('a')] += 1
# 检查初始窗口是否是异位词
if s_count == p_count:
ans.append(0)
# 滑动窗口检查s的剩余部分
for i in range(1, s_len - p_len + 1):
# 移除窗口最左边的字符
s_count[ord(s[i - 1]) - ord('a')] -= 1
# 添加窗口最右边的字符(注意索引)
s_count[ord(s[i + p_len - 1]) - ord('a')] += 1
# 检查新窗口是否是异位词
if s_count == p_count:
ans.append(i)
return ans
# 测试用例
arr = find_anagrams('cbaebabacd', 'abc')
print({"arr": arr}) # 输出: {'arr': [0, 6]}
关注我的公众号,回复 100905A2 获取hot100算法在线链接