C++ STL 棧及其應用

一、棧的概念

棧
棧是一種特殊的線性表,其插入操作和刪除操作限定在表的一端進行,這一段被稱爲“棧頂”(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取模),再輸出

後綴表達式

分析:可以遍歷一遍,把數找出來入棧,如果是符號,從棧裏彈出兩個做運算(注意要下面的-上面的(下面的/上面的))

中綴表達式轉後綴表達式

分析,用了這個方法

  1. 先定義一個棧,用來存運算符,遍歷一遍中綴表達式
  2. 如果這個字符是數字,則輸出
  3. 否則,如果是運算符的話,
    3.1. 如果棧爲空,或棧頂運算符爲左括號“(”,則直接將此運算符入棧
    3.2. 如果優先級比棧頂運算符的優先級高,也將運算符壓入棧
    3.3.將棧頂的運算符彈出並輸出,再次轉到分析的第4行與棧中新的棧頂運算符相比較。
  4. 否則(是括號)
    4.1 如果是左括號,直接壓入棧
    4.2 如果是右括號,則依次彈出棧頂的運算符,並輸出,直到遇到左括號爲止,此時將左括號彈出(因爲右括號沒有入棧)

還有一道題,也是表達式求值(有+ - * /),我是用了前面兩個的結合加判錯:(自己試試,不給代碼)

中綴表達式值(expr)
--------------------------------------分割線--------------------------------------

分析:可以用dfs(會超時),把還沒入棧的隊和棧作爲參數,可以有兩種操作:1.把棧頂彈出(需要判斷是否爲空)2.把待入棧的隊列裏面的第一個入棧。

驗證棧序列

分析(一組數據):可以把輸入的出棧序列的值改爲在入棧序列中的位置,然後遍歷b數組,如果已入棧, 則看棧頂是否是它,如果不是,可以直接輸出“No”,看下一組數據了。如果未入棧,那就把它前面的都入棧,最後輸出“Yes”。

出棧序列

分析:我這裏用一個計數器來記錄出棧了幾個,如果都出棧了,就結束循環

  1. 在大小爲(c-棧的大小)的窗口裏找最小的
  2. 如果棧非空
    2.1 如果這個數比棧頂元素還小, 把前面的都入棧,當前元素輸出
    2.2 否則,輸出棧頂元素並出棧
  3. 否則,把前面的都入棧,當前元素輸出
  4. 移動到下一個窗口

自己嘗試做一做吧~

代碼

表達式括號匹配(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;
}

棧的應用還有很多,先不說了,拜拜~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章