Daily算法刷题【面试经典150题-2️⃣】
文章目录
- ⚡️滑动窗口
- 1.长度最小的子数组
- 2.无重复字符的最长子串
⚡️滑动窗口
1.长度最小的子数组
🎈法1:暴力枚举
TLE
/*
时间复杂度:O(n2)
空间复杂度:O(1)
*/
class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int ans = INT_MAX;long sum = 0;int n = nums.size();for(int i = 0; i < n; i++){sum = 0;for(int j = i; j < n; j++){sum += (long)nums[j];if(sum >= target){ans = min(ans, j - i + 1);break; //!!!找到最近的就break}}}return ans == INT_MAX ? 0 : ans;}
};
🎈法2:滑动窗口
定义两个指针 l 和 r 分别表示子数组(滑动窗口窗口)的开始位置和结束位置
里面数的和就是就是要求的子数组的和
/*
时间复杂度:O(n)
空间复杂度:O(1)
*/
class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {long sum = 0;int n = nums.size();for(int i = 0; i < n; i++){sum += (long)nums[i];}if(sum < target) return 0;int l = 0,r = 0;int cur = 0;int ans = INT_MAX;while(r < n){cur += nums[r];while(cur >= target){ans = min(r - l + 1, ans);cur -= nums[l];l++;}r++;}return ans;}
};
🎈法3:前缀和 + 二分
其实就是把暴力的o(n2)优化掉一个为o(nlogn),再确定完子数组的开始后,查找结束r需要o(n),我们采用二分查找这个满足的最小的r,因为数组中所有数都是大于0的,所以前缀和是单调的,因此可以利用前缀和
class Solution {
public:int minSubArrayLen(int s, vector<int>& nums) {int n = nums.size();if (n == 0) {return 0;}vector<int> sums(n+1, 0);for(int i = 1; i <= n; i++){sums[i] = sums[i - 1] + nums[i - 1];}int ans = INT_MAX;for (int i = 0; i < n; i++) {int l = i + 1, r = n, pos = -1;while (l <= r) {int mid = (l + r) >> 1;if (sums[mid] - sums[i] >= s) {pos = mid;r = mid - 1; // 继续向左收缩} else {l = mid + 1;}}if(pos != -1) ans = min(ans,pos - i);}return ans == INT_MAX ? 0 : ans;}
};
2.无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
🎈法1:滑动窗口
维护一个窗口,使里面的数据始终满足要求,一般来说就是用l,r分别表示区间的起点和终点,r一直往后窗口不断增大,当不满足条件时l++,窗口缩小
当l=0,r=3时,a重复,此时就应该l++,从窗口中取出重复的a
/*时间复杂度:O(n)
*/
class Solution {
public:int lengthOfLongestSubstring(string s) {int n = s.size();unordered_set<char> set;int l = 0, r = 0;int ans = 0;while(r < n){// 去重while(set.find(s[r])!=set.end()){set.erase(s[l]);l++;}ans = max(ans, r - l + 1);set.insert(s[r]);r++;}return ans;}
};