相向双指针
- 167.两数之和 ll - 输入有序数组 leetcode.cn/problems/tw… 给出了一个整数数组,非递减顺序排列,找到两个数字之和等于target
2 3 4 6 8 target = 9
暴力: 依次进行枚举,两层for循环,时间复杂度为
暴力做法忽视了题目中给出的已经排好序的数组,随机选择3和8,相加等于11大于9,也就代表这两个数字中间任意2数字相加大于9。
我们选择2和8,相加等于10大于9,那么代表8与2之后数字相加也就大于9,所以去除8,到6。 2和6相加等于8,小于9,那么去除2,向下一位置。 找到3和6,返回数组。 时间复杂度
获取多少信息量,暴力做法花费O(1)时间知道了O(1)信息,优化后,剩下最小和最大相加,就知道了和剩下的数字相加大于9或者小于9,O(1)时间得知O(n)信息。前提是:数组排好序
left = 0
right = len(numbers) - 1
while left < right:
s = numbers[left] + numbers[right]
if s == target:
break
if s > target:
right -= 1
else:
left += 1
return [left + 1, right + 1]
- 15.三数之和 leetcode.cn/problems/3s…
一个无序整数数组,判断是否存在三个数字和为target。
这道题关键在于连续三个值,并且这三个值要将正负分开,才能更简单
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort() #排序之后才能使用相向指针
ans = []
n = len(nums)
for i in range(n - 2):
x = nums[i]
if i > 0 and x == nums[i - 1]:
continue
j = i + 1
k = n - 1
while j < k:
s = x + nums[j] + nums[k]
if s > 0:
k -= 1
elif s < 0:
j += 1
else:
ans.append([x, nums[j], nums[k]])
j += 1
while j < k and nums[j] == nums[j - 1]:
j += 1
k -= 1
while k > j and nums[k] == nums[k + 1]:
k -= 1
return ans
- 11.盛最多水的容器 leetcode.cn/problems/co…
随便选择一个边,还是同时相向移动
class Solution:
def maxArea(self, height: List[int]) -> int:
ans = 0
left = 0
right = len(height) - 1
while left < right:
area = (right - left) * min(height[left], height[right])
ans = max(ans, area)
if height[left] < height[right]:
left += 1
else:
right -= 1
return ans
时间复杂度
- 42.接雨水 leetcode.cn/problems/tr…
# 前后缀分解
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
pre_max = [0] * n
pre_max[0] = height[0]
for i in range(1, n):
pre_max[i] = max(pre_max[i - 1], height[i])
suf_max = [0] * n
suf_max[-1] = height[-1]
for i in range(n - 2, -1, -1):
suf_max[i] = max(suf_max[i + 1], height[i])
ans = 0
for h, pre, suf in zip(height, pre_max, suf_max):
ans += min(pre, suf) - h
return ans
相向双指针是为了优化空间复杂度
# 相向双指针
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
ans = 0
left = 0
right = n - 1
pre_max = 0
suf_max = 0
while left <= right:
pre_max = max(pre_max, height[left])
suf_max = max(suf_max, height[right])
if pre_max < suf_max:
ans += pre_max - height[right]
right -= 1
return ans
课后作业:
- 16.最接近的三数之和 leetcode.cn/problems/3s…
- 18.四数之和 leetcode.cn/problems/4s…
- 2824.统计和小于目标的下标对数目 leetcode.cn/problems/co…
- 611.有效三角形的个数 leetcode.cn/problems/va…
同向双指针
【同向双指针滑动窗口【基础算法精讲 03】】 www.bilibili.com/video/BV1hd…
- 209.长度最小的子数组 leetcode.cn/problems/mi…
暴力做法是每次向左或者向右扩展,计算,但是忽略了数组都是正数
子数组右端点是不断扩展,左端点不断收缩
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
n = len(nums)
ans = n + 1
s = 0
left = 0
for right, x in enumerate(nums): # x: nums[right]
s += x
# 循环方法一:
# while s - nums[left] >= target:
# s -= nums[left]
# left += 1
# if s >= target:
# ans = min(ans, right - left + 1)
# 循环方法二:把ans更新放在循环内
while s > target:
ans = min(ans, right - left + 1)
s -= nums[left]
left += 1
return ans if ans <= n else 0
- 3.无重复字符的最长子串 leetcode.cn/problems/lo…
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
ans = 0
cnt = Counter() # hashmap char int
left = 0
for right, c in enumerate(s):
cnt[c] += 1
while cnt[c] > 1:
cnt[s[left]] -= 1
left += 1
ans = max(ans, right - left + 1)
return ans
- 713.乘积小于 K 的子数组 leetcode.cn/problems/su…
class Solution:
def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
if k <= 1:
return 0
ans = 0
prod = 1
left = 0
for right, x in enumerate(nums):
prod *= x
while prod >= k:
prod /= nums[left]
left += 1
ans += right - left + 1
return ans