像栈实现队列,队列实现栈这种,蛮简单的,写一下吧还是
栈实现队列
class MyQueue {
public:
MyQueue() {}
void push(int x) {
stk1.push(x);
}
int pop() {
while (!stk1.empty()) {
stk2.push(stk1.top());
stk1.pop(); // 弹出 不返回
}
int out = stk2.top(); // 返回
stk2.pop(); // 弹出
while (!stk2.empty()) {
stk1.push(stk2.top());
stk2.pop(); // 弹出
}
return out;
}
int peek() {
while (!stk1.empty()) {
stk2.push(stk1.top());
stk1.pop(); // 弹出 不返回
}
int out = stk2.top(); // 返回
while (!stk2.empty()) {
stk1.push(stk2.top());
stk2.pop(); // 弹出
}
return out;
}
bool empty() {
if (stk1.empty()) return true;
else return false;
}
private:
stack<int> stk1;
stack<int> stk2;
};
队列实现栈
class MyStack {
public:
MyStack() { // 为什么不能在构造函数里面定义队列啊,我觉得吧,是这里面只能初始化,不能定义}
void push(int x) {
que_1.push(x);
}
int pop() {
int out1 = this->top();
for (int n = que_1.size(); n > 1; n--) {
que_2.push(que_1.front());
que_1.pop();
}
que_1.pop();
while (!que_2.empty()) {
que_1.push(que_2.front());
que_2.pop();
}
return out1;
}
int top() {
return que_1.back();
}
bool empty() {
if (que_1.empty()) return true;
else return false;
}
private:
queue<int> que_1, que_2;
};
20. 有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 5:
- 输入: "{[]}"
- 输出: true
首先,括号匹配是使用栈解决的经典问题,好神奇,栈这么有用啊,一开始没思路,看了一下题解,一个好的数据结构果然可以提高效率
写的时候有个错误 错误1:if (s[i] == ')' && stks.top() == '(' && !stks.empty())
显示: Segmentation fault(段错误),这是对内存操作不当造成的
错误原因:在栈为空的时候判断if(stks.top() == '(')
修改:&&是短路求值,当左侧无法确定结果的时候才计算右侧的值, 所以说先判断栈是否为空,若为空就不进行第三个判断,因此调换if中后两个判断的顺序
例子:
stack<char> stks;
if (stks.top() == '(') s.push_back('a'); // 在栈为空的时候就会报这样的错误
解题过程
class Solution {
public:
bool isValid(string s) {
stack<char> stks;
if (s.size() % 2) return false; // 基数个直接返回
for (int i = 0; i < s.size(); i++) {
if (s[i] == ')' && !stks.empty() && stks.top() == '(') { // "){"会报错,为什么,
stks.pop();
continue;
}
else if (s[i] == ']' && !stks.empty()&& stks.top() == '[') {
stks.pop();
continue;
}
else if (s[i] == '}' && !stks.empty()&& stks.top() == '{') {
stks.pop();
continue;
}
stks.push(s[i]);
}
if (stks.empty()) return true;
return false;
}
};
1047. 删除字符串中的所有相邻重复项
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入: "abbaca"
输出: "ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
是一维消消乐,但是假一维消消乐,因为只能是两两相同配对删除,如果出现奇数个就只删前偶数个,"aaa" 要输出"a",艹,我以为连着就要删呢,原来是偶数个删除,"bbba"要输出"ba"
用数据结构来解决,也就是用栈来解决,把元素一个一个放到栈里,栈top和i指针指向的元素对比,若相同,i++,且栈pop,之后继续元素入栈
跟前面的“【算法】哈希表 汇总”用哈希表数据结构的来解决问题一样,不过注意只删偶数个
class Solution { // 原来是假消消乐
public:
string removeDuplicates(string s) {
stack<char> st1, st2;
for (int i = 0; i < s.size(); ) {
if (!st1.empty() && st1.top() == s[i]) { // 发现潜在的一连串相同了
// while (i < s.size() && st1.top() == s[i]) { // 加上这个就是真实消消乐
i++;
// }
st1.pop();
continue;
}
st1.push(s[i]);
i++;
}
while (!st1.empty()) {
st2.push(st1.top());
st1.pop();
}
string sout;
while (!st2.empty()) {
sout.push_back(st2.top());
st2.pop();
}
return sout;
}
};
239. 滑动窗口最大值
做的第一个困难题
给你一个整数数组 nums,有一个大小为 k **的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入: nums = [1,3,-1,-3,5,3,6,7], k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
图片思路:
新设计一个单调队列:单调递减或单调递增的队列
其实就是步骤多一点,不过要用到优先级队列的小顶堆;大顶堆(优先级队列)来存放这个窗口里的k个数字,这样就可以知道最大的最大值是多少了, 但是问题是这个窗口是移动的,而大顶堆每次只能弹出最大值,我们无法移除其他数值, 这样就造成大顶堆维护的不是滑动窗口里面的数值了。所以不能用大顶堆,要用小顶堆
常用的queue在没有指定容器的情况下,deque就是默认底层容器
class Solution { // 真不愧是hard啊
public:
class mDeqQue {
public:
// mDeqQue(); // 可以不用写构造函数
// 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
// 同时pop之前判断队列当前是否为空。
void pop(int val) {
if (!deq.empty() && deq.front() == val) deq.pop_front();
}
// 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
int front() {
return deq.front();
}
// 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
// 这样就保持了队列里的数值是单调从大到小的了。
void push(int val) {
while(!deq.empty() && val > deq.back()) {
deq.pop_back();
}
deq.push_back(val);
}
private:
deque<int> deq;
};
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
mDeqQue nb_deqque;
vector<int> outmx;
// 先放前k个,第一个窗口
for (int i = 0; i < k; i++) {
nb_deqque.push(nums[i]);
}
outmx.push_back(nb_deqque.front());
// 之后一个一个放
for (int i = k; i < nums.size(); i++) {
// 错误1:滑动窗口移除最前面元素
// 防止元素重复 如 i=3 应该对比的是nums[0] ,而不是nums[i - 1]
nb_deqque.pop(nums[i - k]);
nb_deqque.push(nums[i]);
outmx.push_back(nb_deqque.front());
}
return outmx;
}
};
# 347.前 K 个高频元素
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
真是要边用边学,通过这道题我熟悉了一下谓词,还有优先级队列,也就是大顶堆 小顶堆,还有pair,在用的过程中学,这种感觉真爽,这样学的好快
class Solution {
// 二元谓词
public:
class mycompar {
public:
// bool operator()(pair<int, int>right, pair<int, int>left) { // 这个也行 但是下面的更好
bool operator()(const pair<int, int>& right, const pair<int, int>& left) {
return right.second > left.second; // 小顶堆先弹出的是最小的
}
};
public:
bool mycompar2(const pair<int, int>& right, const pair<int, int>& left) {
return right.second > left.second;
} //为什么不可以是这种谓词啊
vector<int> topKFrequent(vector<int>& nums, int k) { //哈希表?
// 出现频率前 k 高
// 不是频率大于k,那unordered_map这个数据结构就不合数了
// 大顶堆 小顶堆,用小顶堆
unordered_map<int, int> numap; // 无序map
vector<int> outun;
priority_queue<pair<int, int>, vector<pair<int,int>>, mycompar> pr_que; // 优先级队列
// 要统计元素出现频率
for (int i = 0; i < nums.size(); i++) {
numap[nums[i]]++;
}
// 用固定大小为k的小顶堆,扫描所有频率的数值
for (unordered_map<int, int>::iterator it = numap.begin(); it != numap.end(); it++) {
pr_que.push(*it);
if (pr_que.size() > k) pr_que.pop(); // 小顶堆先弹出的是最小的
}
// 找出前K个高频元素,因为小顶堆先弹出的是最小的,
// 所以倒序来输出到数组for (int i = k - 1; i >= 0; i--) 但下面我这样也行,因为可以按任意顺序 返回答案
for (int i = 0; i < k; i++) {
outun.push_back(pr_que.top().first); // 错误1:不能用pr_que.front(),因为优先级队列压根就没有这个函数
pr_que.pop();
}
return outun;
}
};