滑动窗口题
LC3 LC424
https://leetcode-cn.com/problems/longest-repeating-character-replacement/solution/fen-xiang-zhen-cang-de-shuang-zhi-zhen-m-fdsk/
首先需要区分两个概念:子串(子数组) 和 子序列。这两个名词经常在题目中出现,非常有必要加以区分。子串sub-string(子数组 sub-array)是连续的,而子序列 subsequence 可以不连续。
3. 无重复字符的最长子串
难度中等4905
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int n = s.size();
int left = 0, right = 0;
int ans = 0;
unordered_set<char> mem;
while(right < n){
while(mem.count(s[right])){
mem.erase(s[left]);
left++;
}
ans = max(ans, right - left + 1);
mem.emplace(s[right]);
right ++;
}
return ans;
}
};
424. 替换后的最长重复字符
难度中等281
给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
注意:字符串长度 和 k 不会超过 104。
class Solution {
public:
int characterReplacement(string s, int k) {
int n = s.size();
int left = 0, right = 0;
int maxnow = 0;
int ans = 0;
vector<int> mem(26);
//右指针维护
while(right < n)
{
mem[s[right] - 'A'] ++ ;
maxnow = max(maxnow, mem[s[right] - 'A']);
//移动left到满足题目条件为止
//本题即是用总长度减去最大出现的数字个数
//令剩下数字的数量要小于k
while(right - left + 1 - maxnow > k){
mem[s[left] - 'A'] --;
left ++;
}
ans = max(ans, right - left + 1);
right ++;
}
return ans;
}
};
以上两题大体模版相同 只在根据题意判断是否符合并且移动左指针时有所不同。
30. 串联所有单词的子串
难度困难409
给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int n = s.size();
int word_len = words[0].size(), all_len = words.size();
vector<int> ans;
int left = 0, right;
//记录原words
unordered_map<string, int> mem;
for(auto& c: words)
mem[c] ++;
unordered_map<string, int> window;
for(int i = 0; i < word_len; ++ i){
while(left + word_len * all_len <= n){
right = left;
while(right <= n){
if(mem == window){
ans.push_back(left);
}
string ns = s.substr(right, word_len);
window[ns] ++;
//不存在或次数超过
if(!mem.count(ns)) break;
if(window[ns] > mem[ns]) break;
right += word_len;
}
window.clear();
left ++;
}
}
return ans;
}
};
995. K 连续位的最小翻转次数
难度困难181
在仅包含 0
和 1
的数组 A
中,一次 _K_
位翻转包括选择一个长度为 K
的(连续)子数组,同时将子数组中的每个 0
更改为 1
,而每个 1
更改为 0
。
返回所需的 K
位翻转的最小次数,以便数组没有值为 0
的元素。如果不可能,返回 -1
。
示例 1:
输入:A = [0,1,0], K = 1
输出:2
解释:先翻转 A[0],然后翻转 A[2]。
示例 2:
输入:A = [1,1,0], K = 2
输出:-1
解释:无论我们怎样翻转大小为 2 的子数组,我们都不能使数组变为 [1,1,1]。
示例 3:
输入:A = [0,0,0,1,0,1,1,0], K = 3
输出:3
解释:
翻转 A[0],A[1],A[2]: A变成 [1,1,1,1,0,1,1,0]
翻转 A[4],A[5],A[6]: A变成 [1,1,1,1,1,0,0,0]
翻转 A[5],A[6],A[7]: A变成 [1,1,1,1,1,1,1,1]
提示:
1 <= A.length <= 30000
1 <= K <= A.length
双指针包括快慢指针和左右指针,其实滑动窗口也是一种双指针,但是几种方法的适用情况都不同。
class Solution {
public:
int minKBitFlips(vector<int>& A, int K) {
int n = A.size();
int ans = 0;
int right = 0;
//用队列来模拟滑动窗口
//存的是下标,表示的是哪些位置(在k范围内)进行翻转
//那么队列的大小n就表示了前k个单位中要翻转几次
//那么n为偶数时当前的数就等于没有翻转,为奇数则翻转
queue<int> que;
while(right < n){
//无法再翻转,或者过了要翻转的数字距离
if(!que.empty() && right >= que.front() + K)
que.pop();
//综合得出的结论,要翻转,记录
if(que.size() % 2 == A[right]){
if(right + K > n)
return -1;
que.push(right);
ans ++;
}
right ++;
}
return ans;
}
};
剑指 Offer II 015. 字符串中的所有变位词
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> ans;
int sLen = s.size(), pLen = p.size();
if(sLen < pLen){
return {};
}
vector<int> sCount(26, 0);
vector<int> pCount(26, 0);
for(int i = 0; i < pLen; ++ i){
sCount[s[i] - 'a'] ++;
pCount[p[i] - 'a'] ++;
}
if(sCount == pCount){
ans.push_back(0);
}
for(int i = 0; i < sLen - pLen; ++ i){
sCount[s[i] - 'a'] --;
sCount[s[i + pLen] -'a'] ++;
if(sCount == pCount){
ans.push_back(i + 1);
}
}
return ans;
}
};
双指针题
11. 盛最多水的容器
难度中等2235
给你 n
个非负整数 a1,a2,...,a``n
,每个数代表坐标中的一个点 (i, ai)
。在坐标内画 n
条垂直线,垂直线 i
的两个端点分别为 (i, ai)
和 (i, 0)
。找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
示例 3:
输入:height = [4,3,2,1,4]
输出:16
示例 4:
输入:height = [1,2,1]
输出:2
提示:
n = height.length
2 <= n <= 3 * 104
0 <= height[i] <= 3 * 104
class Solution {
public:
int maxArea(vector<int>& height) {
int res = 0;
int n = height.size();
int left = 0, right = n - 1;
while(left < right){
int high = min(height[left], height[right]);
int width = right - left;
res = max(res, high * width);
if(height[left] == high){
left ++;
}else{
right --;
}
//好像不是很能找到滑动的情况
// if(height[left] == high){
// left ++;
// }
}
return res;
}
};
15. 三数之和
难度中等3030
给你一个包含n
个整数的数组nums
,判断nums
中是否存在三个元素 a,b,c ,使得 _a + b + c = _0 ?请你找出所有和为0
且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
vector<vector<int> > ans;
if(n < 2) return ans;
sort(nums.begin(), nums.end());
for(int i = 0; i < n - 2; ++ i){
if(nums[i] > 0)break;
if(i > 0 && nums[i] == nums[i - 1]) continue;
int low = i + 1, high = n - 1;
while(low < high){
int temp = nums[i] + nums[low] + nums[high];
if(temp == 0){
ans.push_back({nums[i], nums[low], nums[high]});
while(low < high && nums[low] == nums[low + 1]) low ++;
while(low < high && nums[high] == nums[high - 1]) high --;
low ++;
high --;
}
if(temp < 0) low ++;
if(temp > 0) high --;
}
}
return ans;
}
};
75. 颜色分类
难度中等801
给定一个包含红色、白色和蓝色,一共 n
个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0
、 1
和 2
分别表示红色、白色和蓝色。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1]
输出:[0,1,2]
示例 3:
输入:nums = [0]
输出:[0]
示例 4:
输入:nums = [1]
输出:[1]
class Solution {
public:
void sortColors(vector<int>& nums) {
int n = nums.size();
int p0 = 0, p2 = n - 1;
int i = 0;
while(i <= p2){
if(nums[i] == 0){
swap(nums[i], nums[p0]);
p0 ++;
//这里必须要i++, 因为p0必然是1,如果是2的话已经被换再i后面了,这点是可以保证的
i ++;
}else if(nums[i] == 1){
i ++;
}else{
swap(nums[i], nums[p2]);
p2 --;
//这里不要i++ 因为我们只能保证nums[i] == 2,但是不能保证nums[p2] == 1
}
}
}
};
141. 环形链表
难度简单986
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos
是 -1
,则在该链表中没有环。注意:**pos**
不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true
。 否则,返回 false
。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
提示:
- 链表中节点的数目范围是
[0, 104]
-105 <= Node.val <= 105
pos
为-1
或者链表中的一个 有效索引 。/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(!head || !head->next){
return false;
}
ListNode* slow = head;
ListNode* quick = head->next;
while(slow != quick){
if(!quick || !quick->next){
return false;
}
slow = slow->next;
quick = quick->next->next;
}
return true;
}
};
142. 环形链表 II
难度中等912
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。
为了表示给定链表中的环,我们使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果pos
是-1
,则在该链表中没有环。注意,**pos**
仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:你是否可以使用
O(1)
空间解决此题?
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
提示:
- 链表中节点的数目范围在范围
[0, 104]
内 -105 <= Node.val <= 105
pos
的值为-1
或者链表中的一个有效索引/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head == nullptr){
return nullptr;
}
ListNode* slow = head;
ListNode* fast = head;
while(fast != nullptr){
slow = slow->next;
if(fast->next == nullptr){
return nullptr;
}
fast = fast->next->next;
if(slow == fast){
//利用公式a = c + (n - 1)(b + c)
ListNode* p = head;
while(p != slow){
p = p->next;
slow = slow->next;
}
return slow;
}
}
return nullptr;
}
};