文章目錄
一、棧的概念
棧是一種特殊的線性表,其插入操作和刪除操作限定在表的一端進行,這一段被稱爲“棧頂”(top),相對的另一端稱爲“棧底”(bottom)。插入操作一般稱之爲“壓棧”(push),刪除操作稱之爲“退棧”(pop)。棧的特點是“先進後出” (LIFO,First In Last Out) 。
二、棧的操作
1.定義
stack<typename> 棧的名字;
其中typename是想要的數據類型。
哦,對了,要使用棧,還需要加上一個頭文件:(加了萬能頭文件就不需要了)
#include <stack>
如果要存int,可以這麼寫:
stack<int> s
2.棧的函數
假設已經定義一個棧,名字是s,那麼就有這些函數:
s.push(x); //插入元素,x表示要插入的值,什麼都行(但是類型必須和定義的相同)
s.pop(); //將棧頂彈出,無返回值
s.empty(); //判斷是否是空棧
s.size(); //返回棧中有多少個元素
s.top(); //返回棧頂
s.swap(s2); //交換s和s2裏面的值(s2需要和s是一個類型)
stack默認是空隊列,如果不想怎麼辦?
假設已經定義了s1是stack<int>類型,那麼可以這麼寫:
stack<int> s2(s1);
注意!stack沒有重載=號!
舉個栗子:(倒序輸出)
#include <iostream>
#include <stack>
using namespace std;
int main(int argc,char* argv[],char** env){
stack<int> s;
for(int i=1;i<=5;i++)
s.push(i);
while(!s.empty()){ //只要非空就循環
cout<<s.top()<<' ';
s.pop(); //這一步千萬不要忘了
}
cout<<endl;
return 0;
}
來幾道題
表達式括號匹配(stack)
分析:不是括號不需要入棧,如果是左括號就入棧,如果是右括號就看棧頂是不是左括號,如果是,就把左括號彈出,否則輸出NO,結束程序。如果最後棧爲空(所有左括號都被彈出了),輸出YES,否則輸出NO。
字符串匹配問題(strs)
分析:其實和上一道差不多,只不過多了幾組數據和優先級,可以一組數據一組數據的做,再弄一個棧,來保存每一個的最大的優先級是多少,在入棧的時候判斷是否可行,如果可行把這個棧加上這個的優先級,在出棧的同時把這個棧也刪一個。
表達式求值
分析:還是先分離,因爲符號肯定比數字少一個,所以循環數字就行了,每循環一次就把這個數入棧,如果符號沒越界,那麼就判斷符號是什麼,如果是“+”,把棧頂的兩個數入棧,否則把這兩個數乘起來在對10000取模,再把結果入棧。因爲棧裏面的數都是要加的,所以把這些數加起來(加的同時要對10000取模),再輸出
後綴表達式
分析:可以遍歷一遍,把數找出來入棧,如果是符號,從棧裏彈出兩個做運算(注意要下面的-上面的(下面的/上面的))
中綴表達式轉後綴表達式
分析,用了這個方法:
- 先定義一個棧,用來存運算符,遍歷一遍中綴表達式
- 如果這個字符是數字,則輸出
- 否則,如果是運算符的話,
3.1. 如果棧爲空,或棧頂運算符爲左括號“(”,則直接將此運算符入棧
3.2. 如果優先級比棧頂運算符的優先級高,也將運算符壓入棧
3.3.將棧頂的運算符彈出並輸出,再次轉到分析的第4行與棧中新的棧頂運算符相比較。 - 否則(是括號)
4.1 如果是左括號,直接壓入棧
4.2 如果是右括號,則依次彈出棧頂的運算符,並輸出,直到遇到左括號爲止,此時將左括號彈出(因爲右括號沒有入棧)。
還有一道題,也是表達式求值(有+ - * /),我是用了前面兩個的結合加判錯:(自己試試,不給代碼)
中綴表達式值(expr)
--------------------------------------分割線--------------------------------------
棧
分析:可以用dfs(會超時),把還沒入棧的隊和棧作爲參數,可以有兩種操作:1.把棧頂彈出(需要判斷是否爲空)2.把待入棧的隊列裏面的第一個入棧。
驗證棧序列
分析(一組數據):可以把輸入的出棧序列的值改爲在入棧序列中的位置,然後遍歷b數組,如果已入棧, 則看棧頂是否是它,如果不是,可以直接輸出“No”,看下一組數據了。如果未入棧,那就把它前面的都入棧,最後輸出“Yes”。
出棧序列
分析:我這裏用一個計數器來記錄出棧了幾個,如果都出棧了,就結束循環
- 在大小爲(c-棧的大小)的窗口裏找最小的
- 如果棧非空
2.1 如果這個數比棧頂元素還小, 把前面的都入棧,當前元素輸出
2.2 否則,輸出棧頂元素並出棧 - 否則,把前面的都入棧,當前元素輸出
- 移動到下一個窗口
自己嘗試做一做吧~
代碼
表達式括號匹配(stack)
#include <iostream>
#include <conio.h>
#include <stack>
using namespace std;
int main(int argc,char* argv[],char** env){
char c;
stack<char> s;
do{
cin>>c;
if(c=='(')
s.push(c); //壓棧
else if(c==')'){
if(!s.empty()&&s.top()=='(')
s.pop(); //把左括號彈出
else{
cout<<"NO\n"; //沒有左括號
return 0;
}
}
}while(c!='@');
if(s.empty()) //如果左括號都彈出了
cout<<"YES\n";
else
cout<<"NO\n";
return 0;
}
字符串匹配問題(strs)
#include <iostream>
#include <conio.h>
#include <stdio.h>
#include <string>
#include <stack>
using namespace std;
int priority(char c) { //返回c的優先級
if(c=='<'||c=='>')
return 1;
if(c=='('||c==')')
return 2;
if(c=='['||c==']')
return 3;
if(c=='{'||c=='}')
return 4;
return -1;
}
bool is_left(char c) { //判斷是否是左括號
return c=='('||c=='['||c=='{'||c=='<';
}
bool is_right(char c) {
return c==')'||c==']'||c=='}'||c=='>';
}
//兩個括號是否匹配
bool match(char a,char b) {
if(!is_left(a)) //如果左邊的不是左括號
return false;
if(a=='(')
return b==')';
if(a=='[')
return b==']';
if(a=='{')
return b=='}';
if(a=='<')
return b=='>';
}
//一個一個判斷
bool func(stack<char>& s,char c,stack<int>& prioritys) {
if(is_left(c)) { //如果是左括號
if(prioritys.top()>=priority(c)) {
s.push(c);
prioritys.push(priority(c));
} else {
return false; //優先級太高
}
} else if(is_right(c)) { //如果是右邊
if(!s.empty()&&match(s.top(),c)) { //如果匹配
s.pop();
if(!prioritys.empty()) //非空
prioritys.pop();
} else
return false;
}
return true;
}
bool check(string str) {
stack<char> s;
stack<int> prioritys; //優先級
prioritys.push(0x7fffffff); //很大很大的數
for(int i=0; i<str.size(); i++) {
char c=str[i];
if(!func(s,c,prioritys)) {
return false;
}
}
return s.empty();
}
int main(int argc,char* argv[],char** env) {
int n;
cin>>n;
getchar(); //以防把n後面的換行當成第一個字符
while(n--) {
string str;
getline(cin,str); //保險,其實完全可以用cin>>str;
if(check(str))
cout<<"YES\n";
else
cout<<"NO\n";
}
return 0;
}
表達式求值
#include <iostream>
#include <string>
#include <stack>
using namespace std;
bool is_digit(char c) {
return c>='0'&&c<='9';
}
string num[100003],operators; //數字和運算符
int calc(string str) { //分離
int n=0,len=str.size();
for(int i=0; i<len; ++i) {
if(is_digit(str[i])) {
num[n]+=str[i];
} else {
n++;
operators+=str[i];
}
}
return ++n; //因爲什麼,所以要加1?
}
int main(int argc,char* argv[],char** env) {
string str;
getline(cin,str);
int n=calc(str);
stack<int> s;
stack<char> op;
for(int i=0; i<n; i++) {
bool flag=false;
//下面用C++11的函數來把num[i]轉換成int,用的要在Dev-C++
//編譯選項->代碼生成/優化->代碼生成->語言標準 設置位ISO C++11或GNU C++11
int x=stoi(num[i]);
s.push(x%10000);
char c;
if(i+1<n)
c=operators.at(i); //或operators[i]
else
flag=true; //最後一個
if(op.empty()||op.top()=='+') {
op.push(c);
} else { //op.top=='*'
s.pop();
int y=s.top();
s.pop();
s.push(x*y%10000);
op.push(c); //別忘了把當前的加進去
}
if(flag) { //最後一個要在這裏判斷,爲什麼自己想想
int sum=0;
while(!s.empty()) {
sum+=s.top()%10000; //其實對不對10000取模都一樣,因爲反正push的時候
//都對10000取模了
sum%=10000; //這裏非常重要,我就是沒寫這個才過不去,如果不寫這個的話下面的
//cout<<sum<<endl;就得改成cout<<sum%10000<<endl;
s.pop();
}
cout<<sum<<endl;
return 0; //加不加隨便,反正都是最後一輪
}
}
return 0;
}
後綴表達式
#include <iostream>
#include <conio.h>
#include <string>
#include <stack>
using namespace std;
bool is_digit(char c) { //判斷是否爲數字,也可以用algorithm中的
return c>='0'&&c<='9';
}
int calc(string input) {
int x=0;
stack<int> s;
for(int i=0; i<input.size(); i++) {
char c=input[i];
if(is_digit(c)) { //如果是數字
x=x*10+c-'0'; //當前的數
} else {
if(c=='@') //結束符
break;
if(c=='.') //分隔符
s.push(x);
x=0; //清0
if(c=='+'||c=='-'||c=='*'||c=='/') { //是運算符
int b=s.top(); //注意要先取b,因爲下面要a+b或a-b或a*b或a/b
s.pop(); //取一個彈出一個
int a=s.top();
s.pop(); //這一步很重要
switch(c) {
case '+': {
s.push(a+b);
break;
}
case '-': {
s.push(a-b);
break;
}
case '*': {
s.push(a*b);
break;
}
case '/': {
s.push(a/b);
break;
}
default: {
break;
}
}
}
}
}
return s.top(); //棧裏面應該只剩結果了
}
int main(int argc,char* argv[],char** env) {
string s;
getline(cin,s); //保險
cout<<calc(s)<<endl;
return 0;
}
中綴表達式轉後綴表達式
#include <iostream>
#include <string>
#include <stack>
using namespace std;
bool is_digit(char c) {
return c>='0'&&c<='9';
}
int priority(char c) { //優先級
if(c=='+'||c=='-')
return 1;
if(c=='*'||c=='/')
return 2;
}
string change(string str){ //把中綴表達式轉成後綴表達式
string ans,x;
stack<char> s;
for(int i=0; i<str.size(); i++) {
char c=str[i];
if(is_digit(c)) {
x+=c;
if(i+1==str.size()||!is_digit(str[i+1])){
ans+=x; //將結果加這個數
ans+=' '; //爲了區分是哪個數
x=""; //清空數
}
} else if(c=='+'||c=='-'||c=='*'||c=='/') {
while(1) {
if(s.empty()||s.top()=='(') {
s.push(c);
break;
} else if(priority(c)>priority(s.top())) {
s.push(c);
break;
} else {
char t=s.top();
s.pop();
ans+=t;
}
}
} else {
if(c=='(') {
s.push(c);
} else { //右括號
while(s.top()!='(') {
char t=s.top();
s.pop();
ans+=t;
}
s.pop(); //將左括號彈出,不要忘了
}
}
}
while(!s.empty()){
ans+=s.top();
s.pop();
}
return ans;
}
int main(int argc,char* argv[],char** env) {
string str;
getline(cin,str); //保險
cout<<change(str)<<endl;
return 0;
}
棧(兩個超時)
#include <iostream>
#include <conio.h>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
int n;
int out[1001]; //已出棧的
unsigned long long cnt=0;
void print(){
//測試
//for(int i=0;i<n;i++)
// cout<<out[i]<<' ';
//cout<<endl;
cnt++;
}
void dfs(int step,stack<int> s,int k,queue<int> q){ //k:out數組中的個數-1
if(s.empty()&&q.empty()){ //找到一種解
print();
return ;
}
if(!s.empty()&&s.top()==n){ //優化(其實也沒多少)
for(int i=k;i<n;i++){
out[i]=s.top();
s.pop();
}
print();
return ;
}
if(s.empty()){ //如果棧爲空,取下一個入棧
s.push(q.front());
q.pop();
dfs(step+1,s,k,q);
} else {
int t=s.top();
s.pop();
out[k]=t;
dfs(step+1,s,k+1,q);
s.push(t); //把t放回去
if(!q.empty()){
s.push(q.front());
q.pop();
dfs(step+1,s,k,q);
}
}
}
int main(int argc,char* argv[],char** env){
queue<int> q; //q:待入站的隊列
stack<int> s;
cin>>n;
for(int i=2;i<=n;i++) //1已經入棧
q.push(i);
s.push(1); //把1入棧,因爲完全可以把1入棧後出棧,所以不用擔心別的數打頭的輸出不出來
dfs(1,s,0,q);
cout<<cnt<<endl; //記得輸出
return 0;
}
驗證棧序列
#include <iostream>
#include <stack>
#include <map>
using namespace std;
bool check(int a[],int b[],bool is_pushed[],int n) {
stack<int> s;
for(int i=1; i<=n; i++) { //遍歷b數組
if(is_pushed[b[i]]) { //b[i]已入棧
if(!s.empty()&&s.top()!=a[b[i]])
return false;
s.pop(); //如果等於,將這個數彈出
} else { //b[i]未入棧
//把a[b[i]]的前面的元素入棧.
int j=b[i],k;
is_pushed[j]=true;
for(k=j-1;k>0;k--) //找到沒入棧的開頭-1
if(is_pushed[k])
break;
for(int l=k+1; l<j; l++) { //把前面的都入棧
s.push(a[l]);
is_pushed[l]=true;
}
}
}
return true;
}
int main(int argc,char* argv[],char** env) {
int n,t;
cin>>t;
while(t--) {
map<int,int> mp; //mp[i]表示i在a數組中的下標
cin>>n;
int a[n+2],b[n+2],x; //a數組記錄入棧序列,b數組存出棧序列的數在a[]中的下標
bool is_pushed[n+2]; //is_pushed[i]記錄a[i]有沒有入棧
for(int i=1; i<=n; i++) {
cin>>a[i];
//初始化
is_pushed[i]=0;
mp[a[i]]=i;
}
for(int i=1; i<=n; i++){
cin>>x;
b[i]=mp[x];
}
if(check(a,b,is_pushed,n)) //這裏傳is_pushed因爲完全可以在輸入的時候初始化,不需要在裏面初始化
cout<<"Yes\n";
else
cout<<"No\n";
}
return 0;
}
出棧序列
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
int main(int argc,char* argv[],char** env){
stack<int> s; //棧裏存在a數組中的下標
int x,n,c,cnt=0;
cin>>n>>c;
int a[n+2],begin=0,end=c-1; //窗口的開頭和結尾
for(int i=0;i<n;i++)
cin>>a[i];
while(cnt<n){
//找a數組中最小
int MinIndex=min(begin,n-1);
for(int i=min(begin,n-1);i<=end&&i<n;i++){
if(a[i]<a[MinIndex])
MinIndex=i;
}
//判斷
if(!s.empty()){ //棧非空
if(a[s.top()]<a[MinIndex]){ //如果棧頂元素比這個數還小
cout<<a[s.top()]<<' '; //出棧
cnt++;
a[s.top()]=0x7fffffff; //記錄已經出棧了
end++;
s.pop();
} else {
//把前面的都入棧
for(int i=begin;i<MinIndex;i++){
if(a[i]<0x7fffffff){
s.push(i);
begin=i;
}
}
if(begin==MinIndex)
begin++;
else
begin+=2;
cout<<a[MinIndex]<<' '; //輸出
cnt++;
a[MinIndex]=0x7fffffff;
end++;
}
} else { //棧爲空
//把前面的都入棧
for(int i=begin;i<MinIndex;i++){
if(a[i]<0x7fffffff){
s.push(i);
begin=i;
}
}
if(begin==MinIndex)
begin++;
else
begin+=2;
cout<<a[MinIndex]<<' '; //也是輸出
cnt++;
a[MinIndex]=0x7fffffff;
end++;
}
}
return 0;
}
棧的應用還有很多,先不說了,拜拜~