算法基础之双指针|豆包MarsCode AI 刷题

81 阅读4分钟

相向双指针

  • 167.两数之和 ll - 输入有序数组 leetcode.cn/problems/tw… 给出了一个整数数组,非递减顺序排列,找到两个数字之和等于target

2 3 4 6 8 target = 9

暴力: 依次进行枚举,两层for循环,时间复杂度为O(n2)O(n^2)

暴力做法忽视了题目中给出的已经排好序的数组,随机选择3和8,相加等于11大于9,也就代表这两个数字中间任意2数字相加大于9。

我们选择2和8,相加等于10大于9,那么代表8与2之后数字相加也就大于9,所以去除8,到6。 2和6相加等于8,小于9,那么去除2,向下一位置。 找到3和6,返回数组。 时间复杂度O(n)O(n)

获取多少信息量,暴力做法花费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]

一个无序整数数组,判断是否存在三个数字和为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

随便选择一个边,还是同时相向移动

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

时间复杂度


# 前后缀分解
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

课后作业:

同向双指针

【同向双指针滑动窗口【基础算法精讲 03】】 www.bilibili.com/video/BV1hd…

暴力做法是每次向左或者向右扩展,计算,但是忽略了数组都是正数

子数组右端点是不断扩展,左端点不断收缩

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
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
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