最近複習到棧。這是一種小巧有用的數據結構,是功能特化的鏈表。一句話概括:LIFO(last in first out)
基本概念:
實現方式:1)鏈式實現 2)順序實現。
鏈表的 "addLast" 和 " removeLast" 操作相當於棧的 "push" 和 "pop"操作。
1)鏈式實現的話,各節點佔用的內存空間不一定連續,我前面的一篇博文:"LinkedList--鏈表" 的代碼中,"addLast" 和 " removeLast" 函數就相當於棧的 "push" 和 "pop"函數,這裏就不贅述了。
2)順序實現的話,各節點佔用的內存空間連續,下面的參考代碼我用靜態數組來實現。
(注:C/C++中的malloc/new既可以用於鏈式實現申請節點空間,也可以用於順序實現申請節點空間。只不過,鏈式實現的時候,一次只申請一個節點空間(下次申請的節點空間就可能連不上了);順序實現的時候,一次申請一段連續的節點空間。另外,不用malloc/new,直接使用靜態數組的話,其效果相當於申請了一段連續的內存空間)
棧的實現:
#ifndef STACK_H_
#define STACK_H_
#include <stdexcept>
#include <iostream>
using namespace std;
const int MAXN = 100;
template<typename T>
class Stack
{
private:
T array[MAXN];
int rear;
public:
Stack();
Stack(T list[], int len);
void push_back(T element);
T pop_back();
T top();
int getSize();
void print();
};
template<typename T>
Stack<T>::Stack() {
rear = 0;
}
template<typename T>
Stack<T>::Stack(T list[] , int len) {
rear = 0;
if(len <= 1 || len >= MAXN)
{
throw runtime_error("Index out of range");
}
for(int i = 0 ; i < len ; i ++)
push_back(list[i]);
}
template<typename T>
void Stack<T>::push_back(T element) {
array[rear++] = element;
}
template <typename T>
T Stack<T>::pop_back() {
if(rear == 0)
throw runtime_error("Index out of range");
return array[--rear-1];
}
template <typename T>
T Stack<T>::top() {
if(rear == 0)
throw runtime_error("Index out of range");
return array[rear-1];
}
template <typename T>
int Stack<T>::getSize() {
return rear;
}
template <typename T>
void Stack<T>::print() {
if(getSize() == 0)
{
cout << "Empty stack\n";
return;
}
int i = 0;
while(i<rear)
cout << array[i++] << " ";
cout << endl;
}
#endif
棧的應用:
括號匹配
/**************************************
*
* copyright: ace_yom (Peizhen Zhang)
* author: ace_yom (Peizhen Zhang)
* date: 2015-8-18
* description: 鏈表實現
*
**************************************/
#include <iostream>
#include <stack>
using namespace std;
stack<char> st;
string str;
int main() {
int i = 0;
cin >> str;
while(i < str.length())
{
if(st.empty())
{
st.push(str[i]);
}
else
{
char ch = st.top();
if((ch == '[' && str[i] == ']') || (ch == '(' && str[i] == ')'))
st.pop();
else
st.push(str[i]);
}
if(!st.empty() && (st.top()==')'|| st.top()==']'))
break;
++ i;
}
cout << (st.empty() ? "Yes" : "No") << endl;
return 0;
}
表達式求值
表達式求值有兩種問題,一種是後綴表達式求值,單棧就可以操作;一種爲中序表達式求值,需要使用 “算法優先分析法” 來確定運算符的優先級,並且需要雙棧。下面僅介紹第一種--後綴表達式求值。第二種問題--中序表達式求值,讀者可以參考文獻[1]。
後綴表達式,又稱逆波蘭表達式。逆波蘭表達式可以表示出所有的表達式,且本身不帶括號:
如 a-b-c,其逆波蘭表達式爲:ab-c-;((a+b)*c - d/e) -f,其逆波蘭表達式爲:ab+c*de/-f-
給定一個輸入序列(初始化爲後綴表達式,一個指針指向輸入序列的),和棧(初始化爲空), '@' 爲取負號操作,它是一個單目運算符,用於本身就是負數的情況,和雙目運算符 '-' 減號分開來。
算法如下所示:
1. 若輸入序列的指針指向一個數字(變量名也表示一個數字),則將它壓入棧。
2. 若輸入序列的指針指向一個操作符:
1. 若操作符爲 ‘@’,則取出棧頂的數字,取反,再壓棧。輸入序列的指針往後移。
2. 若操作符不是 '@',則取出棧頂的兩個數字,做這個操作,然後把結果壓入棧。輸入序列的指針往後移。
用棧求後綴表達式:
/***********************************
*
* copyright: ace_yom (Peizhen Zhang)
* author: ace_yom (Peizhen Zhang)
* date: 2015-8-20
* description: 棧求後綴表達式的值
*
**********************************/
#include <iostream>
#include <string>
#include <stack>
using namespace std;
string str;
stack<int> st;
//算法僅支持一位整數,因爲重點在於“用棧求後綴表達式的值”
//至於多位整數、浮點數的情況,需要作一些小處理,讀者可自行解決
int main() {
cin >> str;
bool tag = true;
if(str[0] < '0' || str[0] > '9')
tag = false;
else
{
int i = 0;
int tmp1,tmp2;
while(i<str.length())
{
char ch = str[i];
if(ch >= '0' && ch <= '9')
{
st.push(int(str[i]-'0'));
cout << int(str[i]-'0') << endl;
}
else
{
switch (ch)
{
case '@':
tmp1 = st.top();
st.pop();
st.push(-1*tmp1);
cout << "middle result: " << -1*tmp2<< endl;
break;
case '-':
tmp1 = st.top();
st.pop();
tmp2 = st.top();
st.pop();
st.push(tmp2-tmp1);
break;
case '+':
tmp1 = st.top();
st.pop();
tmp2 = st.top();
st.pop();
st.push(tmp2+tmp1);
break;
case '*':
tmp1 = st.top();
st.pop();
tmp2 = st.top();
st.pop();
st.push(tmp2*tmp1);
break;
case '/':
tmp1 = st.top();
st.pop();
tmp2 = st.top();
st.pop();
st.push(tmp2/tmp1);
break;
default:
tag = false;
break;
}
}
i ++;
if(tag == false)
break;
}
}
if(!tag)
cout << "Invalid Expression !\n";
else
{
cout << "The result is " << st.top() << endl;
st.pop();
}
return 0;
}
References:
[1] 數據結構(C 語言版) 嚴蔚敏 吳偉民 編著--3.2.5 表達式求值