最近复习到栈。这是一种小巧有用的数据结构,是功能特化的链表。一句话概括: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 表达式求值