栈与队列
缺省情况:指默认设置的情况
我们常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的低层结构。
SGI STL中队列一样是以deque为缺省情况下的底部结构。
使用两个栈实现先进先出队列
在push数据的时候,只要数据放进输入栈就好,但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。
最后如何判断队列为空呢?如果进栈和出栈都为空的话,说明模拟的队列为空了。
queue<int >q;
MyStack() {
}
void push(int x) {
q.push(x);
}
int pop() {
int size=q.size()-1;//让除最后一个元素返回队列,让放出此时的队头元素!
while(size--){
q.push(q.front());
q.pop();
}
int n=q.front();
q.pop();
return n;
}
int top() {
return q.back();//直接使用back返回最后一个
}
bool empty() {
if(!q.empty()) return false;
return true;
}
括号序列
class Solution {
public:
bool isValid(string s) {
stack<int> st;
for (int i = 0; i < s.size(); i++) {//没有遍历完整个序列的情况
if (s[i] == '(') st.push(')');//对左括号的三种情况 遇到就压入对应的右括号
else if (s[i] == '{') st.push('}');
else if (s[i] == '[') st.push(']');
// 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false
// 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false
else if (st.empty() || st.top() != s[i]) return false;//对于右括号的情况,还没遍历完,如果栈为空(多余的右括号)或者右括号和栈顶元素不相同,返回false
else st.pop(); // st.top() 与 s[i]相等,栈pop弹出元素
}
// 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
return st.empty();//做完就返回
}
消消乐,删除字符串中所有相邻重复相邻项
class Solution {
public:
string removeDuplicates(string S) {
stack<char> st;
for (char s : S) {//遍历s
if (st.empty() || s != st.top()) {
st.push(s);//如果s不和top相等,或者栈为空的话都要直接压入
} else {
st.pop(); // s 与 st.top()相等的情况,就抛出
}
}
string result = "";//放入字符串中
while (!st.empty()) { // 将栈中元素放到result字符串汇总
result += st.top();
st.pop();
}
reverse (result.begin(), result.end()); // 栈的顺序不一样,此时字符串需要反转一下
return result;
}
};
使用string类型直接作为栈操作
string removeDuplicates(string S) {
string result;
for(char s : S) {
if(result.empty() || result.back() != s) {//res.back牛!
result.push_back(s);//队列了吧?string 没有push_front操作
}
else {
result.pop_back();//没有pop_front的操作
}
}
return result;
}
逆波兰表达式
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;//使用栈
for (int i = 0; i < tokens.size(); i++) {//遍历
if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") {
int num1 = st.top();//遇到操作符进入运算
st.pop();
int num2 = st.top();
st.pop();
if (tokens[i] == "+") st.push(num2 + num1);//注意!!!是先第二个数然后再第一个数
if (tokens[i] == "-") st.push(num2 - num1);
if (tokens[i] == "*") st.push(num2 * num1);
if (tokens[i] == "/") st.push(num2 / num1);
} else {//遇到数字压入数字
st.push(stoi(tokens[i]));//stoi函数可以从这个结点的字符窜遇到的数字(终止为第一个不是数字的字符)转换为int,这个东西的开始点貌似只是传入的地址的值是数字或者下一个是位置数字才可以
}
}
int result = st.top();
st.pop(); // 把栈里最后一个元素弹出(其实不弹出也没事)
return result;
}
};
那么这个维护元素单调递减的队列就叫做单调队列,即单调递减或单调递增的队列
pop(value):如果窗口移除的元素value等于单调队列的出口元素(最大值),那么队列弹出元素,否则不用任何操作
push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止(此步关键,保证了下次移动时候的队列出队元素为最大值)(问什么是这个说法而不是排除全部元素,因为当队列未满的时候,最大值还不是顶部的元素,这个相当于是初始话的操作)
保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。上面的抽出与答案没关系,使用这个函数()直接与答案
class Solution {
private:
class MyQueue { //单调队列(从大到小)
public://类内的类
deque<int> que; // 使用deque来实现单调队列
// 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
// 同时pop之前判断队列当前是否为空。
void pop(int losevalue) {//定义一个函数,传入一个窗口丢失的值,窗口向右走
if (!que.empty() && losevalue == que.front()) {//队列为空,而且如果这个值等于队列前面的值的时候
que.pop_front();//那么说明需要移除队列的这个值,其实由于下面已经判断了是递减的序列,因此这个操作只能是对最大值才作用,对其他值不会造成任何影响
}
}
// 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
// 这样就保持了队列里的数值是单调从大到小的了。
void push(int newvalue) {//传入一个值,窗口想右走进来的值
while (!que.empty() && newvalue > que.back()) {//队列非空,而且当前传入的值是最大的,清理最后一个一直清理直到保证单调递减(值<最后一个的值)
que.pop_back();//
}//如果是递减的话 就可以直接放进去
que.push_back(newvalue);
//保持递减让这个头值是最大值
}
// 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
int front() {
return que.front();//第一个就是滑动窗口的最大值
}
};
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue que;//相当于结构体 que是一个结构体变量,而这个变量有自己的函数
vector<int> result;
for (int i = 0; i < k; i++) { // 先将前k的元素放进队列
que.push(nums[i]);
}
result.push_back(que.front()); // result 记录前k的元素的最大值
for (int i = k; i < nums.size(); i++) {
que.pop(nums[i - k]); // 滑动窗口移除最前面元素
que.push(nums[i]); // 滑动窗口前加入最后面的元素
result.push_back(que.front()); // 记录对应的最大值
}
return result;
}
};
前k个高频元素
class Solution {
public:
// 小顶堆
class mycomparison {
public:
bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
return lhs.second > rhs.second;//排序是按照second的值排的
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
// 要统计元素出现频率
unordered_map<int, int> map; // map<nums[i],对应出现的次数>
for (int i = 0; i < nums.size(); i++) {
map[nums[i]]++;//对应的值的频率加1
}
// 对频率排序
// 定义一个小顶堆,大小为k
priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;//定一个,以mycmp的方式排序的队列
//数据类型是pair,底层容器是pair类型的数组,盲猜是因为数据类型是这个,所以底层容器变成pair需要对应这个
// 用固定大小为k的小顶堆,扫面所有频率的数值
for (unordered_map<int, int>::iterator it = map.begin(); it != map.end(); it++) {
pri_que.push(*it);//每次放入map的值,
if (pri_que.size() > k) { // 如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
pri_que.pop();//弹出最上面的
}
}
// 找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒叙来输出到数组
vector<int> result(k);
for (int i = k - 1; i >= 0; i--) {
result[i] = pri_que.top().first;//保存是用键值
pri_que.pop();
}
return result;//返回一个数组
}
};