栈与队列理论基础
栈是先进后出,队列是先进先出。
栈和队列是STL(C++标准库)里面的两个数据结构。
SGI STL 由Silicon Graphics Computer Systems公司参照HP STL实现,被Linux的C++编译器GCC所采用,SGI STL是开源软件,源码可读性甚高。
栈提供push和pop等等接口,必须满足先进后出,不提供走访功能,也不提供迭代器(iterator)。不像set和map一样可以用迭代器iterator来遍历所有元素。栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。
所以STL中的栈不是容器,而是容器适配器,container adapter。栈的底层可以是vector,deque,list,主要是数组和链表的底层表现。
可以指定vector为栈的底层实现,初始化语句如下:
std::stack<int, std::vector<int> > third; // 使用vector为底层容器的栈
指定list 为起底层实现,初始化queue的语句如下:
std::queue<int, std::list<int>> third; // 定义以list为底层容器的队列
STL中的栈也不是容器,而是容器适配器,container adapter。
232.用栈实现队列
使用栈实现队列的下列操作:
push(x) -- 将一个元素放入队列的尾部。
pop() -- 从队列首部移除元素。
peek() -- 返回队列首部的元素。
empty() -- 返回队列是否为空。
class MyQueue {
stack<int> s1;
stack<int> s2;
public:
MyQueue() {
}
void push(int x) {
s1.push(x);
}
int pop() {
int temp;
if(s2.empty()){
while(!s1.empty()){
s2.push(s1.top());
s1.pop();
}
}
int last = s2.top();
s2.pop();
while(!s2.empty()){
s1.push(s2.top());
s2.pop();
}
return last;
}
int peek() {
int temp;
if(s2.empty()){
while(!s1.empty()){
s2.push(s1.top());
s1.pop();
}
}
int last = s2.top();
while(!s2.empty()){
s1.push(s2.top());
s2.pop();
}
return last;
}
bool empty() {
return s1.empty();
}
};
- 时间复杂度: push和empty为O(1), pop和peek为O(n)
- 空间复杂度: O(n)
225. 用队列实现栈
使用队列实现栈的下列操作:
- push(x) -- 元素 x 入栈
- pop() -- 移除栈顶元素
- top() -- 获取栈顶元素
- empty() -- 返回栈是否为空
只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作
class MyStack {
queue<int> que;
public:
MyStack() {
}
void push(int x) {
que.push(x);
}
int pop() {
int size = que.size(); //5
size--;//4
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();
}
};
- 时间复杂度: pop为O(n),其他为O(1)
- 空间复杂度: O(n)
20. 有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 注意空字符串可被认为是有效字符串。
第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false
第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false
第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false
class Solution {
public:
bool isValid(string s) {
int len = s.size();
stack<char> st;
for(int i=0; i<len; i++){
if(s[i]=='('||s[i]=='{'||s[i]=='['){
st.push(s[i]);
}else{
if(!st.empty()){
if(s[i]==')'&&st.top()=='('){
st.pop();
}else if(s[i]==']'&&st.top()=='['){
st.pop();
}else if(s[i]=='}'&&st.top()=='{'){
st.pop();
}else{
return false;
}
}else{
return false;
}
}
}
if(st.empty()) return true;
return false;
}
};
- 时间复杂度: O(n)
- 空间复杂度: O(n)
1047. 删除字符串中的所有相邻重复项
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
class Solution {
public:
string removeDuplicates(string s) {
stack<char> st;
for(int i=0; i<s.size(); i++){
if(st.empty()){
st.push(s[i]);
}else{
if(s[i]==st.top()){
st.pop();
}else{
st.push(s[i]);
}
}
}
string res;
int len = st.size();
for(int k=0; k<len; k++){
res.push_back(st.top());
st.pop();
}
reverse(res.begin(),res.end());
return res;
}
};
- 时间复杂度: O(n)
- 空间复杂度: O(n)
当然可以拿字符串直接作为栈,这样省去了栈还要转为字符串的操作。
class Solution {
public:
string removeDuplicates(string S) {
string result;
for(char s : S) {
if(result.empty() || result.back() != s) {
result.push_back(s);
}
else {
result.pop_back();
}
}
return result;
}
};
- 时间复杂度: O(n)
- 空间复杂度: O(1),返回值不计空间复杂度