基础算法一:同向双指针
Posted Pistachiout
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础算法一:同向双指针相关的知识,希望对你有一定的参考价值。
同向双指针——滑动窗口
讲解实例:LeetCode209. 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
- 暴力双循环 时间复杂度O(n^2).
- 同向双指针
就举示例1的例子,设左右指针初始都为0,想要找>=7的最短子数组,可以遍历每个右端点,找到以每个数为右端点的最短数组。
在右指针向右遍历的过程中,若数组小于target=7则右指针继续向右移动
我们假设现在取到了2 3 1 2即2为右端点,这是大于等于7的,那么为了取到最短的数组,我们可以将左指针向右移动,这些就可以缩短数组,直到数组<7即取到3 1 2的情况,这时我们已经知道了以2为右端点的数组>=7最短为2 3 1 2 ,长度为4
之后我们继续遍历右指针,向右移动,此时左端点为3 ,且此时我们的左指针不需要回退,因为我们已知 2 3 1 2>7,若左指针回退,而右指针却向右移动,则必然不是最短的子数组,故此时我们的数组为3 1 2 4>7,故向之前一样,左指针向右移动,直到数组<7即取到2 4的情况,这时我们已经知道了以4为右端点的数组>=7最短为1 2 4 ,长度为3
右指针继续向右,此时3为数组右端点,2为左端点, 此时我们判断数组2 4 3>7,那么我们和之前一样,向右移动左指针,直到<7,此时我们知道以3为右端点的数组最短为4 3。
这就是同向双指针的具体流程,右指针与左指针的移动距离都最多为n,故时间复杂度为O(n).
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
n = len(nums)
ans = n + 1 # 也可以写 inf
s = left = 0
for right, x in enumerate(nums):
s += x
# while s - nums[left] >= target:
# s -= nums[left]
# left += 1
# if s >= target:
# ans = min(ans, right-left+1)
while s >= target: # 满足要求时循环
ans = min(ans, right - left + 1)//满足条件,每次循环开始取最小值
s -= nums[left]
left += 1
return ans if ans <= n else 0
练习LeetCode713. 乘积小于 K 的子数组
给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。
与上一题一样,遍历右端点,乘积>=k时左端点右移
而需要注意的是子数组数目,假设此时数组为5 2 6,此次<k,那么以6为右端点的<k的子数组为[5,2,6],[2,6],[6]
故总结若以 right 为端点,则left向右移到<k后,所有的[left —right],[left+1 —right]·····[right-1,right],[rigth]都满足条件,即为right-left+1
func numSubarrayProductLessThanK(nums []int, k int) (ans int)
if k <= 1
return
prod, left := 1, 0
for right, x := range nums
prod *= x
for prod >= k //不满足条件时循环
prod /= nums[left]
left++
ans += right - left + 1//在满足条件后取加法
return ans
练习LeetCode3. 无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
func lengthOfLongestSubstring(s string) (ans int)
l := 0
cnt:=[128]int
for r,c:=range s
cnt[c]++
for cnt[c]>1//不满足条件时循环
cnt[s[l]]--
l++
ans = max(ans,r-l+1)//在满足条件后取最大值
return ans
func max(a, b int) int if b > a return b ; return a
练习LeetCode1004. 最大连续 1 的个数
给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k 个 0 ,则返回 数组中连续 1 的最大个数 。
最多翻转 k 个 0,即数组中最多允许有k个0
func longestOnes(nums []int, k int) int
l,ans,cnt:=0,0,0
for r,num:=range nums
if num==0
cnt++
for cnt>k //不满足条件时循环
if nums[l]==0
cnt--
l++
ans=max(ans,r-l+1)//在满足条件后取最大值
return ans
func max(a, b int) int if a < b return b ; return a
练习LeetCode1234. 替换子串得到平衡字符串
有一个只含有 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符,且长度为 n 的字符串。假如在该字符串中,这四个字符都恰好出现 n/4 次,那么它就是一个「平衡字符串」。给你一个这样的字符串 s,请通过「替换一个子串」的方式,使原字符串 s 变成一个「平衡字符串」。你可以用和「待替换子串」长度相同的 任何 其他字符串来完成替换。请返回待替换子串的最小可能长度。如果原字符串自身就是一个平衡字符串,则返回 0。
func balancedString(s string) int
left,m:=0,len(s)/4
cnt:=[100]int
ans:=1000001
for _,c:=range s
cnt[c]++
if cnt['Q'] == m && cnt['W'] == m && cnt['E'] == m && cnt['R'] == m
return 0 // 已经符合要求啦
for right,c:=range s
cnt[c]--
for cnt['Q'] <= m && cnt['W'] <= m && cnt['E'] <= m && cnt['R'] <= m//满足条件
ans=min(ans,right-left+1)
cnt[s[left]]++
left++
return ans
func min(a,b int)intif a>b return b return a
总结:
求最小值时,一般是遍历数组由满足条件到不满足条件,右指针每次遍历时取左指针最后一次满足条件的最小结果,即在左指针循环内部
求最大值时,一般是遍历数组由不满足条件到满足条件,右指针每次遍历时取左指针第一次满足条件的最大结果,即在左指针循环退出后
以上是关于基础算法一:同向双指针的主要内容,如果未能解决你的问题,请参考以下文章
算法专题(02)双指针(01) 简单LeetCode 977
算法入门 03双指针(简单 - 第二题)LeetCode 283
leetcode 524. 通过删除字母匹配到字典里最长单词双指针,在不同字符串中同向查找
leetcode 524. 通过删除字母匹配到字典里最长单词双指针,在不同字符串中同向查找