代码随想录【day 10 栈与队列】| 232.用栈实现队列、 225. 用队列实现栈
理论基础
- 栈和队列是STL(C++标准库)两个数据结构
- 队列是先进先出
- 栈是先进后出
- 栈提供push和pop等接口,所有元素必须符合先进后出规则,栈不提供走访功能,也不提供迭代器(iterator)。
- 栈的底层实现可以是
vector,deque,list
Q1: STL中栈和队列不被归类为容器,而被归类为
container adapter(容器适配器)
Q2: 三个最为普遍的STL版本为HP STL 、P.J.Plauger STL、SGI STL(SGI STL是开源软件,源码可读性甚高)
Q3: 常用的SGI STL 如果没有指定底层实现的话,默认是以deque
为缺省情况下栈的底层结构
LeetCode 232.用栈实现队列
题目链接:232.用栈实现队列
卡哥文解
视频讲解
题目描述: 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
- 实现 MyQueue 类:
- void push(int x) 将元素 x 推到队列的末尾
- int pop() 从队列的开头移除并返回元素
- int peek() 返回队列开头的元素
- boolean empty() 如果队列为空,返回 true ;否则,返回 false
解题思路(两个栈模拟队列)
1、假设所有操作都是有效的(例如一个空的队列不会调用pop或者peek操作)
2、使用两个栈(in入栈 out出栈)
实现队列先进先出的特点 注:将 入栈里 所有元素都放到 出栈里
3、push数据:只要数据放进 入栈 stIn.push(x);
4、pop元素:出栈如果为空 就把入栈数据全部导入,再从 出栈弹出栈顶元素 result = stOut.top(); 如果输出栈不为空,直接从出栈弹出数据。
5、判断队列为空:入栈和出栈都为空
实现难点
1、栈的标准操作包括
初始化一个栈
stack< int> stIn;
push元素 将对象压入栈顶
stIn.push(x);
判断为空
stIn.empty()
获得栈顶元素
stIn.top()
弹出栈顶元素
stIn.pop();
返回栈中元素的个数
stIn.size();
2、peek()的实现中复用了pop() (具体实现思路为先调用pop() 再将弹出的元素添加回去)
全局函数内无法直接调用struct类型变量内的成员,必须传一个struct指针(this指针)
this->加上成员名 就是调用这个对象的成员
代码实现
class MyQueue {
public:
// 初始化 入栈 & 出栈
stack<int> stIn;
stack<int> stOut;
MyQueue() {
}
void push(int x) {
stIn.push(x);
}
int pop() {
// 当stOut为空 再从stIn导入数据
if(stOut.empty()){
while(!stIn.empty()){
stOut.push(stIn.top());
stIn.pop();
}
}
int result = stOut.top();
stOut.pop();
return result;
}
int peek() {
int res = this->pop();
stOut.push(res);
return res;
}
bool empty() {
return stIn.empty() && stOut.empty();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
LeetCode 225. 用队列实现栈
题目链接:LeetCode225.用队列实现栈
卡哥文解
视频讲解
- 题目描述: 请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)
- 实现 MyStack 类:
- void push(int x) 将元素 x 压入栈顶。
- int pop() 移除并返回栈顶元素。
- int top() 返回栈顶元素。
- boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
解题思路(一个队列模拟栈)
- 将队列头部元素(除最后一个元素外)重新添加到队列尾部
实现难点
- C++ queue用法:只能访问queue的第一个和最后一个元素,只能在容器末尾添加新元素,只能从头部移除元素。
移除第一个元素
que.pop();在尾部添加一个新的元素
que.push(x);访问第一个元素
que.front();访问最后一个元素
que.back()
代码实现
class MyStack {
public:
// 初始化 队列
queue<int> que;
MyStack() {
}
void push(int x) {
que.push(x);
}
int pop() {
// 将队列头部元素(除最后一个元素外)重新添加到队列尾部
int size = que.size();
size--;
while(size--){
que.push(que.front());
que.pop();
}
int result = que.front();
que.pop();
return result;
}
int top() {
return que.back();
}
bool empty() {
return que.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
day10总结复盘
1、之前对于队列和栈的理解只停留在先进先出以及先进后出的层面,写完这两题利用代码实现了一些基本操作 加深了理解
2、需要用两个栈来模拟队列,而可以只用一个队列来模拟栈。
补充
操作系统中堆(heap)和栈(stack)都是指内存空间。
- 不同的堆为按需申请、动态分配,例如C中的malloc函数和C++中的new操作。(例如:p1 = (char *)malloc(10); )
- 堆基本上可以理解为当前可以使用的空闲内存,但是其申请和释放都要程序员进行代码管理。(如果使用后忘记释放,会造成所谓的内存泄漏问题)
- 栈是程序运行时自动拥有的一小块内存,由编译器自动分配释放,存放函数的参数值、局部变量的值(int a;)当这个局部变量离开其作用域后,所占用的内存就会自动释放。
- 栈的空间有限制,一旦局部变量申请过多(例如开了超大数组),或者函数调用太深(例如递归太多次)就会导致栈溢出。
- 生长方向:堆生长(扩展)方向向上,向着内存地址增加的方向;栈生长(扩展)方向向下,向着内存地址减小的方向增长。
参考文献
https://programmercarl.com/%E6%A0%88%E4%B8%8E%E9%98%9F%E5%88%97%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html
https://zhuanlan.zhihu.com/p/88151799