20. 有效的括号:有效的括号
思路:
1.想要保证实现“有效的括号”,那么就要配对,即需要先入后出---->栈
2.需要检查什么样的错误:括号得不到匹配(落单);类型匹配不对;括号多余/冗余
3.ps:虽然匹配的是相同的类型,但是也存在左右之分,故应该有六种,同时左必先于右
bool isValid(string s) {
stack<char> stChar;
for(int i = 0; i < s.size(); i++) {
if(s[i] == '(' || s[i] == '[' || s[i] == '{') { //左括号存起来
stChar.push(s[i]);
} else if(s[i] == ')' && !stChar.empty()){ //遇到右括号,则去查左括号
if(stChar.top()== '(') stChar.pop();
else return false;
} else if(s[i] == ']' && !stChar.empty()){
if(stChar.top()== '[') stChar.pop();
else return false;
} else if(s[i] == '}' && !stChar.empty()){
if(stChar.top()== '{') stChar.pop();
else return false;
} else { //一开始并无这句,那么就忽略了一种情况
stChar.push(s[i]); //即一来是就是右括号,但栈中并没有数据
}
}
if(stChar.empty()) {
return true;
} else {
return false; // 可以优化为 return stChar.empty()
}
1047. 删除字符串中的所有相邻重复项:删除字符串中所有相邻的重复项
思考:看起来和上题相近似,所以仍然采用栈 stack 进行处理
ps:1.因为随时进行消除相邻重复项,因此stack栈中,有时候就会为空,这就需要进行是否为空的判断
2.对于字符串的处理 +=,而不能相int 类型数组的处理
string removeDuplicates(string S) {
if(S.size() <= 1) return S;
stack<char> st;
string result = "";
st.push(S[0]);
for(int i = 1; i < S.size(); i++) {
if(st.empty() || S[i] != st.top()) { //因此需要判断st是否为空
st.push(S[i]);
} else {
cout << "________________" <<endl;
cout << st.top() << endl;
cout << "________________" <<endl;
st.pop();
}
}
// for(int i = 0; i < st.size(); i++) {
// result[i] = st.top();
// st.pop();
// }
while(!st.empty()) { //将栈中的数据拿出来
result += st.top();
st.pop();
}
reverse(result.begin(), result.end()); //将顺序颠倒回来
return result;
150. 逆波兰表达式求值:逆波兰表达式求值
感觉和上面两道题相近,重要的是了解逆波兰表达式是怎样的。
int evalRPN(vector<string>& tokens) {
stack<long long> 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);
else if(tokens[i] == "-") st.push(num2 - num1);
else if(tokens[i] == "*") st.push(num2 * num1);
else if(tokens[i] == "/") st.push(num2 / num1);
}else { //遇到数字,那么进行类型转换后进行入栈
st.push(stoll(tokens[i]));
}
}
int result = st.top();
st.pop();
return result;
}
用栈实现队列,用队列实现栈
这两个的关键是要了解两个数据结构特点,栈:先入后出;队列:先入先出
232.用栈实现队列:用栈实现队列
用一个栈,就很难实现“先入先出”的操作,因此需要两个栈来实现这个操作
入栈:当队列push时,则放进到数组中
出栈:当需要队列弹出pop时,从该栈进行出数
class MyQueue {
public:
stack<int> stIn;
stack<int> stOut;
MyQueue() {
}
void push(int x) {
stIn.push(x);
}
int pop() {
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();
}
};
225. 用队列实现栈 :用队列实现栈
似乎使用一个队列无法实现栈的先入后出功能(队列先入先出,顺序不会变),因此还需要使用两个队列、
push empty 函数都是利用队列比较容易实现的
top获取栈顶元素,可以用队列的queue.back();
pop弹出栈顶元素,则需要先把前面的项移走后,留下最后一个元素,再调用queu.pop()
移走前面的函数,就需要使用到第二个函数了,即起到保存前面数据的作用;
class MyStack {
public:
queue<int> que1;
queue<int> que2;
MyStack() {
}
void push(int x) {
que1.push(x);
}
int pop() {
int i = que1.size();
i--; //提前减一,以保留下最后一个数留在队列中
while(i--) {
que2.push(que1.front());
que1.pop();
}
int result = que1.front();
que1.pop();
que1 = que2; //赋回queue
while(!que2.empty()) { //清空备份的queue
que2.pop();
}
return result;
}
int top() {
return que1.back();
}
bool empty() {
return que1.empty();
}
};