两点法(Two Pointer Approach),也就是双指针法,它一般可以把复杂度从穷举法的O(n^2)减小到O(n)
两点法非常有用,很多问题都可以用,如快慢指针,还有求排序数组中的两数和问题
两点法对应了两个指针,左右指针之间也就是一个窗口,当左右指针变化时,也就对应着窗口的滑动。所以实际上滑动窗口法是属于两点法的。
滑动窗口法(Sliding Window)一般是通过左右指针的变化使窗口一直向右滑,同时检测窗口内元素是否满足要求,而两点法的使用相对是更广泛的,是针对两个不同指针的操作,如快慢指针并不能算作滑动窗口法,但也是两点法的应用。
Minimum Size Subarray Sum (leetcode 209)
Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn’t one, return 0 instead.
Example:
Input: s = 7, nums = [2,3,1,2,4,3]
Output: 2
Explanation: the subarray [4,3] has the minimal length under the problem constraint.
class Solution(object):
def minSubArrayLen(self, s, nums):
minlen = float('inf')
left, right= 0, 0
while right<len(nums):
num_sum = sum(nums[left:(right+1)])
if num_sum>=s:
minlen=min(minlen,right+1-left)
left+=1
else:
right+=1
return 0 if minlen==float('inf') else minlen
上述代码是典型的两点法的写法,包括left, right两个指针,但求和部分可以优化,因为有很大部分求和重复在做,优化如下
class Solution(object):
def minSubArrayLen(self, s, nums):
minlen = float('inf')
left, num_sum= 0, 0
for i in range(len(nums)):
num_sum += nums[i]
while num_sum>=s:
minlen=min(minlen,i+1-left)
num_sum -= nums[left]
left+=1
return 0 if minlen==float('inf') else minlen
优化后求和的每一步只需要加减一个数,之前的right 变成了循环中的 i 变量。
Minimum Window Substring (leetcode 76)
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
Example:
Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"
这道题是leetcode中hard等级的,其实可以想到用两点法/滑动窗口法做,但具体操作并不容易想到(腾讯笔试遇到这个类似的题,gg),幸好leetcode上有大神已经帮我们整理好了子串搜索这一类问题的模板解:Sliding Window algorithm template to solve all the Leetcode substring search problem
这里再结合代码解释一下这道题
class Solution(object):
def minWindow(self, s, t):
str_dict={}
for i in t: #建立目标子串的字符频数字典
if i in str_dict:
str_dict[i]+=1
else:
str_dict[i]=1
left, cnt = 0, 0 # left为左指针,cnt为当前窗口中包含的目标子串的字符个数
minlen = float('inf')
res = ""
for i in range(len(s)): # 右指针
if s[i] in str_dict:
str_dict[s[i]] -= 1
if str_dict[s[i]] >= 0:
cnt += 1
# 上面部分是右指针一直在向右移动,直到窗口中包含了所有目标子串字符
# 然后开始左指针向右移动,尝试减小窗口的大小
while cnt==len(t):
if i-left+1<minlen:
minlen=i-left+1
res = s[left:left+minlen]
if s[left] in str_dict:
str_dict[s[left]]+=1
if str_dict[s[left]] > 0:
cnt -= 1
left+=1
return res
举例. S = "ABAACBAB" T = "ABC"
.
找到一个解并记录下之后,会重复上面的步骤
图片参考:https://leetcode.com/problems/minimum-window-substring/solution/
最后附:
一个两点法的总结可以参见:two pointer的运用
快慢指针参考:链表环的入口结点