灵神LeetCode笔记(二)——同向双指针 滑动窗口

1. 209 长度最小的子数组

1.1 题目描述

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0 。

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 104

1.2 思路

1.2.1 暴力法

我们设置循环变量为i,在遍历nums的过程中,不断向右扩展得到的子数组,判断该子数组之和是否满足>=target。如果满足再判断它的长度是否比当前得到的最小长度要小,如果是,则将该子数组长度保存,具体实现看1.3

1.2.2 同向双指针+滑动窗口

核心:由于该题目数组元素都是正数且寻找的是子数组(连续),在遍历过程中,不用一个一个进行扩展,而是用上次遍历扩展得到的结果再包括当前元素,因为上次得到的子数组元素和>=target,再加上一个正数肯定也满足>=target

设置双指针left和right,初始值都为0,即表示nums的第一个元素下标。

将right作为循环变量进行遍历nums数组,在遍历过程中,保存当前left到right对应的子数组元素之和为s,并判断s是否大于等于target。

如果s>=target表示当前子数组是满足条件的子数组,如果当前子数组长度小于我们之前得到的子数组长度的最小值,保存该长度。

此时可以不断缩小数组的左端点,即left++,如果left到right对应的子数组仍然满足条件,判断它长度如果比之前小,就保存他的长度,直到不满足条件为止。

不满足条件之后回到主循环,right++,如此往复

以示例1举例子

1.初始状态如下:

2. right作为循环变量不断遍历nums,直到满足left到right对应的子数组之和满足>=target

        此时left=0 right为3

3. 由于已经满足了条件,不断缩小左端点,left++,直到不满足条件为止。在left++过程中得到满足条件的子数组,不断更改当前的res

4. 不幸left++一次就不满足条件了,所以res没变,此时回到主循环,将right++到满足条件为止

5. 再次将左端点缩小 left++,到不满足条件为止,此时res=3 也就是对应的[1,2,4]子数组

6. 回到主循环,right++

7.不断缩小左端点,left++,此时res=2,对应的子数组就是[4,3]

8.退出循环 返回res

1.3 代码(C++版本)

1.3.1 暴力法

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int res = nums.size()+1;
        for(int i = 0; i < nums.size(); i++) {
            int s = 0;
            for(int j = i; j < nums.size(); j++) {  
                s += nums[j];
                if(s >= target) {
                    res = min(j-i+1, res);
                }
            }
        }

        return res >= nums.size()+1 ? 0 : res;
    }
}

1.3.2 双指针+滑动窗口

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int res = nums.size()+1;
        int left = 0; 
        int s = 0;
        for(int right = 0; right < nums.size(); right++) {
            s += nums[right];
            while(s >= target) {
                res = min(res, right-left+1);
                s -= nums[left];
                left++;
            }
            
        }

        return res >= nums.size()+1 ? 0 : res;
    }
};

 

1.4 时间复杂度和空间复杂度

时间复杂度:O(N)

空间复杂度:O(1)

2. 713 乘积小于等于K的子数组

2.1 题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。

示例 1:

输入:nums = [10,5,2,6], k = 100
输出:8
解释:8 个乘积小于 100 的子数组分别为:[10]、[5]、[2]、[6]、[10,5]、[5,2]、[2,6]、[5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。

示例 2:

输入:nums = [1,2,3], k = 0
输出:0

提示: 

  • 1 <= nums.length <= 3 * 104
  • 1 <= nums[i] <= 1000
  • 0 <= k <= 106

2.2 思路

参考上面长度最小的子数组,这题也是设置同向双指针,其中一个作为循环变量,主动移动,另一个根据是否满足条件调整移动。注意在移动过程中,使用一个变量记录当前子数组的乘积,使用一个变量记录结果,出现一个满足条件的子数组就使结果+1。

k<=1时单独考虑

2.3 代码(C++)

class Solution {
public:
    int numSubarrayProductLessThanK(vector<int>& nums, int k) {
        if(k <= 1) {
            return 0;
        }
        int res = 0;
        int left = 0;
        int prod = 1;
        for(int right = 0; right < nums.size(); right++) {
            prod *= nums[right];
            while(prod >= k && nums[left] > 0) {
                prod /= nums[left];
                left++;
            }
            res += right - left + 1;
        }

        return res;
    }
};

2.4 时间复杂度和空间复杂度

时间复杂度:O(N)

空间复杂度:O(1)

3.  3 无重复字符的最长子串

3.1 题目描述

给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示:

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

3.2 思路

思路与上两题类似,只是设置了一个map用于保存每个字符出现的个数

如果当前right对应的字符有重复,则left++

3.3 代码(C++)

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char, int> count;
        int res = 0;
        int left = 0;
        for(int right = 0; right < s.size(); right++) {
            count[s[right]]++;
            while(count[s[right]] > 1) {
                //就是当前right的元素出现了两次 有重复
                //移除左边元素
                count[s[left]]--;
                left++;
            }
            res = max(res, right-left+1);
        }
        return res;
    }
};

3.4 时间复杂度和空间复杂度

时间复杂度:O(N)

空间复杂度:O(1)/O(128)/O(len(set(s)))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值