堆栈 栈的概念 栈(Stack)是操作受限的线性表 ,插入 和删除 数据元素的操作只能在线性表的一端进行。
当然,你也可以简单理解栈为一个很多层的篮子,一层篮子只能存放一个数据,我们对这个篮子能处理的就只有最上面的那个数据。
往篮子里放入数据的过程就叫做插入 (入栈,进栈,压栈)
把篮子最上面的那一层数据拿出的过程就叫做删除 (出栈,弹栈)
栈的主要操作 入栈(Push) 出栈(Pop)
操作特性:后进先出 栈的顺序储存结构 顺序栈—-栈的顺序储存结构
指针top指示栈顶元素在数组中的位置
进栈:top+1
栈空:top = -1
栈出:top-1
栈满:top = MAX_SIZE
栈的上溢与下溢 在顺序栈中有”上溢”和”下溢”的概念。
顺序栈好比一个盒子,我们在里面放了一叠书,当我们要用书的话只能从最上面一本开始拿,那么当我们把书本放到这个栈中超过盒子的顶部时就放不下了,这时就是“上溢”,“上溢”也就是栈顶指针指出栈的外面,显然时出错了。
反之,当栈中已经没有书时,我们再去拿,发现已经没有书了,这就是“下溢”。“下溢”本身可以表示栈为栈空,因此可以用它来作为控制转移的条件。
如图
1.$a_N,…,a_2,a_1$用来储存数据。
2.定义一个top变量储存栈顶指针的位置。
3.定义一个MAX_SIZE表示栈的最大容量。
上溢的判断 当栈为空时,top的值为-1.当往栈中压入一个元素时,top的值就会加1.
这样,a[0]就代表第一个进栈的元素,a[i-1]代表第i个进栈的元素,a[top]则表示栈顶的元素。
当top=MAX_SIZE-1时,表示栈满。如果再有元素进栈时,则栈会溢出,这时称为”上栈”。
下栈的判断 反之,当top=-1时,再将栈顶元素弹出,就要发生”下溢”
栈类的定义 栈类的定义(以储存字符型数据为例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const int MAX_SIZE = 100 ; class Stack { private : char * data; int size; int top; public : Stack (); Stack (int s); ~Stack (); void push (char ch) ; char pop () ; char getTop () ; bool isEmpty () ; bool isFull () ; void setNull () ; }
构造函数 1 2 3 4 5 Stack::Stack (){ size = MAX_SIZE; top = -1 ; data = new char [MAX_SIZE]; }
1 2 3 4 5 Stack::Srack (int s){ size = s; top = -1 ; data = new char [size]; }
析构函数 1 2 3 Stack::~Stack(){ delete[]data; //内存回收 }
入栈 1 2 3 4 5 6 7 8 9 10 11 12 13 Stack::push (char ch){ if (isFull ()) { cout<<"栈已满,无法入栈" <<endl; } else { top++; data[top] = ch; cout<<"已将元素" <<ch<<"压入栈中" <<endl; cout<<"此时top=" <<top<<endl; } }
出栈并返回栈顶元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Stack::pop () { if (!isEmpty ()) { cout<<"出栈中" <<endl; top--; cout<<"此时top=" <<top<<endl; return data[top+1 ]; } else { cout<<"栈已为空,无法继续出栈" <<endl; } }
获得栈顶元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Stack::getTop () { if (!isEmpty ()) { cout<<"获得栈顶元素中..." <<endl; cout<<"此时top=" <<top<<endl; cout<<"栈顶元素为" ; return data[top]; } else { cout<<"栈已为空,无法获得栈顶元素" <<endl; } }
栈是否为空 1 2 3 4 5 6 7 Stack::isEmpty () { if (top = -1 ) return 1 ; else return 0 ; }
栈是否满 1 2 3 4 5 6 7 8 9 Stack::isFull { if (top >= size - 1 ) { return 1 ; } else return 0 ; }
设置栈为空 memset函数 1 void *memset (void *s, int ch, size_t n) ;
s中当前位置开始向后的n个字节中,把n个字节的每个字节 都替换成ch。
1 2 3 char str[] = "This is a new world" ;memset (str,'#' ,6 );cout<<str<<endl;
输出为
1 2 3 4 5 6 7 Stack::setNull (int len) { memset (data,0 ,len *sizeof (char )); this ->top = -1 ; cout<<"此时top=" <<top<<endl; }
用异常捕获优化顺序栈 函数中最好不要有cout的输出语句,为保证类能多次重用。
输入输出的语句在display或者main函数中即可。
用try…catch语句 1 2 3 4 5 6 7 8 9 10 11 12 13 try { 语句1 ; 语句2 ; 语句3 ; ... } catch (异常类型){ 异常处理代码 } ... catch (异常处理){ 异常处理代码 }
自定义异常类的使用 定义一场内部类(在类的声明部分定义)
丢出异常类(在pop函数遇到空栈时的语句)
捕获异常类(在调用pop函数时,用try/catch捕获) 1 2 3 4 5 6 try { stack.pop (); } catch (Stack::Empty){ cout<<"Stack Empty!!" <<endl; }
用类模板实现顺序栈 通用的栈类 DataType就是我们要使用的数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 template <class DataType >class Stack {public : Stack (); ~Stack (); int empty () ; int pop (DataType & x) ; int push (DataType x) ; DataType top () ; void print () ; private : DataType * data; int len; };
成员函数定义 1 2 3 4 5 6 7 8 9 10 11 template <class DataType >Stack<DataType>::Stack (){ ...... } ...... template <class DataType >void Stack<DataType>::push (DataType e){...... } ...... };
有类Stack的地方,后面都要改成Stack
为了使用类模板对象,必须显示指定模板参数
1 2 Stack<char > charStack; Stack<int > intStack;
存在问题 (44条消息) c++模板学习10之类模板分文件编写_热爱编程的大忽悠的博客-CSDN博客
双端堆栈(两栈共享存储空间) 优点:提高空间利用率 两栈共享空间:使用一个数组来存储两个栈,让一个栈的栈底为该数组的始端,另一个栈的栈底为该数组的末端,两个栈从各自的端点向中间延伸。
性质 栈1为空
栈2为空
栈满
两栈共享空间类的声明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const int Stack_Size=100 ; class BothStack { public : BothStack ( ); ~BothStack ( ); void Push (int num, DataType x) ; DataType Pop (int num) ; DataType GetTop (int num) ; bool isEmpty (int num) ; private : DataType data[Stack_Size]; int top1, top2; };
当然,也可以用类模板去实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const int Stack_Size=100 ; template <class T >class BothStack { public : BothStack ( ); ~BothStack ( ); void Push (int i, T x) ; T Pop (int i) ; T GetTop (int i) ; bool Empty (int i) ; private : T data[Stack_Size]; int top1, top2; };
插入 1 2 3 4 5 6 7 8 9 1. 如果栈满,则抛出上溢异常;2. 判断是插在栈1还是栈2; 2.1 若在栈1插入,则 2.1.1 top1加1; 2.1.2 在top1处填入x; 2.2 若在栈2插入,则 2.2.1 top2减1; 2.2.2 在top2处填入x;
代码实现
1 2 3 4 5 6 7 8 9 10 11 template <class T > void BothStack<T>::Push (int i, T x ){ if (top1==top2-1 ) throw "上溢" ; if (i==1 ) data[++top1]=x; if (i==2 ) data[--top2]=x; }
删除 1 2 3 4 5 6 1.若是在栈1删除,则 1.1 若栈1为空栈,抛出下溢异常; 1.2 删除并返回栈1的栈顶元素; 2.若是在栈2删除,则 2.1 若栈2为空栈,抛出下溢异常; 2.2 删除并返回栈2的栈顶元素;
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 template <class DataType >DataType BothStack<DataType>::Pop (int i){ if (i == 1 ) { if (top1 == -1 ) throw "下溢" ; return data[top1--]; } if (i == 2 ) { if (top2 == StackSize) throw "下溢" ; return data[top2++]; } }
判断每个栈空 1 2 3 4 5 6 7 8 9 10 11 12 13 14 template <class DataType > bool BothStack<DataType>::Empty (int i){ if (i == 1 ){ if (top1 == -1 ) return 1 ; else return 0 ; } if (i == 2 ) { if (top2 == StackSize) return 1 ; else return 0 ; } }
取某个栈顶 1 2 3 4 5 6 7 8 9 10 11 12 template <class DataType > DataType BothStack<DataType>::GetTop (int i) { if (i==1 ) { if (top1!=-1 ) return data[top1]; } if (i==2 ) { if (top2!=StackSize) return data[top2]; } }
压栈 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 template <class DataType >void BothStack<DataType>::push (int num, DataType x){ if (isFull (num)){ throw BothStack<DataType>::Full (); } else { if (num == 1 ){ top1++; data[top1] = x; } else if (num == 2 ){ top2--; data[top2] = x; } } }
出栈 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 template <class DataType >DataType BothStack<DataType>::pop (int num){ if (num == 1 ){ if (isEmpty1 (num)){ throw BothStack<DataType>::Empty1 (); } else { return data[top1--]; } } else if (num == 2 ){ if (isEmpty2 (num)){ throw BothStack<DataType>::Empty2 (); } else { return data[top2++]; } } }
取栈顶元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 template <class DataType >DataType BothStack<DataType>::getTop (int num){ if (num == 1 ){ if (isEmpty1 (num)){ throw BothStack<DataType>::Empty1 (); } else { return data[top1]; } } else if (num == 2 ){ if (isEmpty2 (num)){ throw BothStack<DataType>::Empty2 (); } else { return data[top2]; } } }
栈的链式储存 通用类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 template <class DataType >class LinkStack {private : typedef struct node { DataType data; struct node *next; }Node; Node *top; public : LinkStack (); ~LinkStack (); void push (DataType x) ; DataType pop () ; DataType getTop () ; bool isEmpty () ; class Empty {}; };
成员函数 初始化链栈 1 2 3 4 template <class DataType >LinkStack<DataType>::LinkStack (){ top == NULL ; }
销毁栈 1 2 3 4 5 6 7 8 9 10 11 template <class DataType >LinkStack<DataType>::~LinkStack (){ Node *q = top; Node *p = top -> next; while (top->next){ q = p ->next; delete (p); p = q ->next; } delete (top); }
压栈 1 2 3 4 5 6 7 8 template <class DataType >void LinkStack<DataType>::push (DataType x){ Node *s ; s = new Node; s->data = x; s->next = top; top = s; }
是否栈空 1 2 3 4 5 6 7 template <class DataType >bool LinkStack<DataType>::isEmpty (){ if (top == NULL ) return true ; else return false ; }
取栈顶元素 1 2 3 4 5 6 7 8 9 10 template <class DataType >DataType LinkStack<DataType>::pop (){ if (isEmpty ()) throw LinkStack<DataType>::Empty (); DataType x = top->data; Node*p = top; top = top ->next; delete p; return x; }
取栈顶元素 1 2 3 4 5 6 7 template <class DataType >DataType LinkStack<DataType>::getTop (){ if (isEmpty ()) throw LinkStack<DataType>::Empty (); DataType x = top->data; return x; }
主函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> #include <stdio.h> #include <string.h> using namespace std;template <class DataType>int main () { typedef LinkStack <int > intStack; int x; intStack s; s.push (1 ); s.push (2 ); s.push (3 ); cout <<"栈顶元素:" << s.getTop () <<endl; try { x = s.pop (); cout << x <<endl; x = s.pop (); cout << x <<endl; x = s.pop (); cout << x <<endl; } catch (intStack::Empty){ cout << "LinkStack is Empty!" <<endl; } return 0 ; }
栈的应用—表达式求值 一、表达式求值的规则 表达式求值是程序设计语言编译中的一个基本问题。它的实现就是对“栈”的典型应用。 首先了解算术四则运算的运算规则: (1)先乘除,后加减。 (2)从左到右计算 (3)先算括号内,再算括号外
任何一个表达式都由操作数(operand)、运算符(operator)和界定符组成:
二、运算符优先级 对于两个相继出现的操作符$\theta_1$和$\theta_2$有三种关系: $\theta_1$ <$\theta_2$ $\theta_1$的优先级低于$\theta_2$ $\theta_1$ =$\theta_2$ $\theta_1$的优先级等于$\theta_2$ $\theta_1$ >$\theta_2$ $\theta_1$的优先级高于$\theta_2$ 由此可以列出“+-*/”之间的优先级。如下图
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 char getPriority (char theta1, char theta2) { const char priority[][7 ] = { { '>' ,'>' ,'<' ,'<' ,'<' ,'>' ,'>' }, { '>' ,'>' ,'<' ,'<' ,'<' ,'>' ,'>' }, { '>' ,'>' ,'>' ,'>' ,'<' ,'>' ,'>' }, { '>' ,'>' ,'>' ,'>' ,'<' ,'>' ,'>' }, { '<' ,'<' ,'<' ,'<' ,'<' ,'=' ,'0' }, { '>' ,'>' ,'>' ,'>' ,'0' ,'>' ,'>' }, { '<' ,'<' ,'<' ,'<' ,'<' ,'0' ,'=' }, }; int index1 = getIndex (theta1); int index2 = getIndex (theta2); return priority[index1][index2]; }
三、算法思路 为实现优先算法,可以使用两个工作栈,一个是OPTR,用于寄存运算符,一个是OPND,用于寄存操作数和运算结果。算法的基本思想是: (1) 首先置操作数栈为空栈,表达式起始符’#’为栈底元素。 (2)依次读入表达式中的每个字符,若是操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先级作相应操作,直至整个表达式求值完毕(OPTR栈顶元素和当前读入的字符均为’#’)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 double getAnswer () { { opter.push ('#' ); int counter = 0 ; char c = getchar (); while (c != '#' || opter.top () != '#' ) { if (isdigit (c)) { if (counter == 1 ) { double t = opval.top (); opval.pop (); opval.push (t * 10 + (c - '0' )); counter = 1 ; } else { opval.push (c - '0' ); counter++; } c = getchar (); } else { counter = 0 ; switch (getPriority (opter.top (), c)) { case '<' : opter.push (c); c = getchar (); break ; case '=' : opter.pop (); c = getchar (); break ; case '>' : char theta = opter.top (); opter.pop (); double a = opval.top (); opval.pop (); double b = opval.top (); opval.pop (); opval.push (calculate (b, theta, a)); } } } return opval.top (); }
四,计算b,theta和a 代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 double calculate (double b, char theta, double a) { switch (theta) { case '+' : return b + a; case '-' : return b - a; case '*' : return b * a; case '/' : return b / a; default : break ; } }
五,获取theta所对应的索引 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 int getIndex (char theta) { int index = 0 ; switch (theta) { case '+' : index = 0 ; break ; case '-' : index = 1 ; break ; case '*' : index = 2 ; break ; case '/' : index = 3 ; break ; case '(' : index = 4 ; break ; case ')' : index = 5 ; break ; case '#' : index = 6 ; default :break ; } return index; }
六,主函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> #include <cstdio> #include <cctype> #include <stack> using namespace std;stack<char > opter; stack<double > opval; int main () ;{ int t; cin >> t; getchar (); while (t--) { while (!opter.empty ())opter.pop (); while (!opval.empty ())opval.pop (); double ans = getAnswer (); cout << ans << endl << endl; getchar (); } return 0 ; }
示例 输入
输出
输入(多位数)
输出