1.队列基础知识
先进先出,题目中用到的主要是头尾操作,栈是只操作栈顶
2.基本题目
3. 无重复字符的最长子串(剑指 Offer 48. 最长不含重复字符的子字符串)
#include <typeinfo>
class Solution {
public:
int lengthOfLongestSubstring(string s) {
//要求是连续的子串!=序列
unordered_map<char,int> hash;
queue<char> q;
int res = 0;
for(auto c:s)//一边入队,一边看新加入的元素是否导致重复,重复了就出队
{
hash[c] ++;
q.push(c);
while(hash[c] > 1) hash[q.front()] --, q.pop();
res = max(res, (int)q.size());//q.size()不能直接比较,要转化成int,本身是unsinged int
}
return res;
}
};
剑指 Offer 59 - II. 队列的最大值
class MaxQueue {
public:
queue<int> q;
deque<int> max_q;
MaxQueue() {
}
int max_value() {
if(max_q.empty()) return -1;
else return max_q.front();
}
//维护一个单调递减的双端队列,最大值直接取头即可
//当一个元素进入队列的时候,它前面所有比它小的元素就不会再对答案产生影响!所以遇到大的替换,遇到小的加入
void push_back(int value) {
q.push(value);
while(!max_q.empty() && value > max_q.back()) max_q.pop_back();//维护一个单调递减的数组
max_q.push_back(value);
}
int pop_front() {
if(q.empty()) return -1;
else
{
int t = q.front();
q.pop();
if(max_q.front() == t) max_q.pop_front();//看影响当前最大值不即可
return t;
}
}
};
剑指 Offer 59 - I. 滑动窗口的最大值(239. 滑动窗口最大值)
//第二次做,稍微有点区别,更好理解
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
if(k == 1) return nums;//如果k==1那么提前把第一个数加入队列后,会无法融入到答案中,所以建议用下面一种方法一起做!
//用一个队列存最大值的下标,维护一个递减的序列,这样如果队列头的下标在窗口内,那就一直是最大值
//需要用双端序列
deque<int> q;
vector<int> res;
q.push_back(0);
for(int i = 1; i < n; i ++)//i-j+1=k则j=i+1-k
{
int j = i + 1 - k;
if(q.front() < j) q.pop_front();//窗口滑过了目前队列头存的最大值,则队列弹出一个
while(!q.empty() && nums[i] > nums[q.back()]) q.pop_back(); //如果新数是最大的,那为空了之后就报错了,所以需要判断下是否为空,为空就不继续弹了
q.push_back(i);
if(j >= 0) res.push_back(nums[q.front()]);
}
return res;
}
};
//第一次做的
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
//求大数:递减序列:这个大数一直在, 则肯定是他,有小数则加入,因为当大数弹出,它有可能是剩下的大数;有更大的数则把比它小的逆序的弹出
//求小数:递增序列:因为这个小数一直在则肯定是最小,有大数加入可能是接下来的最小,有小数加入需要把逆序的弹出
deque<int> q;
vector<int> res;
for(int i = 0; i < nums.size(); i ++)
{
int j = i - k + 1;
if(!q.empty() && j > q.front()) q.pop_front();//队头:超过时需要弹出队头,第一个数就不会被弹出了
while(!q.empty() && nums[i] >= nums[q.back()]) q.pop_back();//队尾:加入的数更大,为了构造递减序列,所以往前一直弹出末尾,插入新数坐标
q.push_back(i);//上面的头尾都是非空的,即空的直接加入,数更小时也直接加入
if(j >= 0) res.push_back(nums[q.front()]);
}
return res;
}
};
127. 单词接龙
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
// 将vector转成unordered_set,提高查询速度
unordered_set<string> wordSet(wordList.begin(), wordList.end());
// 如果endWord没有在wordSet出现,直接返回0
if (wordSet.find(endWord) == wordSet.end()) return 0;
// 记录word是否访问过
unordered_map<string, int> visitMap; // <word, 查询到这个word路径长度>
// 初始化队列
queue<string> que;
que.push(beginWord);
// 初始化visitMap
visitMap.insert(pair<string, int>(beginWord, 1));
while(!que.empty()) {
string word = que.front();
que.pop();
int path = visitMap[word]; // 这个word的路径长度
for (int i = 0; i < word.size(); i++) {
string newWord = word; // 用一个新单词替换word,因为每次置换一个字母
for (int j = 0 ; j < 26; j++) {
newWord[i] = j + 'a';
if (newWord == endWord) return path + 1; // 找到了end,返回path+1
// wordSet出现了newWord,并且newWord没有被访问过
if (wordSet.find(newWord) != wordSet.end()
&& visitMap.find(newWord) == visitMap.end()) {
// 添加访问信息
visitMap.insert(pair<string, int>(newWord, path + 1));
que.push(newWord);
}
}
}
}
return 0;
}
};
438. 找到字符串中所有字母异位词
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int>ans;
if(p.size()>s.size()||s.size()==0) return ans;
vector<int>need(128);
vector<int>windows(128);
for(char a:p) need[a]++;
for(int i=0;i<p.size()-1;i++) windows[s[i]]++;
int l=0,r=p.size()-1;//窗口是[l,r]
while(r<s.size())
{
cout<<l<<r<<endl;
for(auto x: windows) cout<<x;
cout<<endl;
windows[s[r++]]++;
if(windows==need) ans.push_back(l);
windows[s[l++]]--;
}
return ans;
}
};