写在前面
在开始前,请牢记这句话:栈是一种后进先出的数据结构,也可简单的理解为一个只能访问顶部的逆序容器。
栈(stack)是限定仅在表的一端进行操作的数据结构,设想一个头插法的单链表且我们只能够对其链表的头结点进行操作,而操作也只能够进行插入(头插法)一个头结点和删除头结点(表中最后一个进去的结点)的数据结构就是栈。
由于栈只能对一端就行操作,因此栈最重要的操作 也就是push(添加元素),top(返回顶元素),pop(顶元素出栈)
栈的主要用处:
①– 深度优先搜索;
②表达式求值;
③子程序/函数调用的管理;
④消除递归
一:栈的介绍
(1)栈分为两种:(1)顺序栈;(2)链式栈
顺序栈定义如下:
链式栈(Linked Stack) – 用单链表方式存储,其中指针的方向是从栈顶向下链接(也就是头插法)
定义如下
顺序栈和链式栈的比较
• 时间效率(两者相近)
– 所有操作都只需常数时间
– 顺序栈和链式栈在时间效率上难分伯仲
• 空间效率(链式稍多开销)
– 顺序栈须说明一个固定的长度
– 链式栈的长度可变,但增加结构性开销
实际应用中,顺序栈比链式栈用得更广泛
– 顺序栈容易根据栈顶位置,进行相对位移,快速定
位并读取栈的内部元素
– 顺序栈读取内部元素的时间为O(1),而链式栈则需
要沿着指针链游走,显然慢些,读取第𝑘个元素需要
时间为𝐎(𝑘)
顺序栈 (Array-based Stack)
① 使用向量(数组)实现,本质上是顺序表的简化版
栈的大小的定义
②关键是确定哪一端作为栈顶 – 上溢,下溢问题
在stack中声明一个链表成员。
(2)添加元素的方法
栈就是一个只有单一出(入)口的容器插入元素**
①顺序栈 就是一直往后添加元素,但是top 一直指向最后添加元素的位置,即可达到后进先出
则push可这样实现
顺序表的push 就是一个头插法的链表(一直在头结点插入节点)
具体实现:
(3)pop弹出最后一个进去的结点(top)
①顺序栈::由于全靠 top下标控制top的位置,所以直接top自减一,虽然这样那个元素还在数组中,但是已经不属于栈了,之后添加元素会覆盖它。
具体实现:
②链式栈:由于是头插法插入的(且头结点保存数据),则直接删掉头结点即可
具体实现:
(4)top返回顶元素
顺序表:直接返回top对应的下标(下标不需要自减1(不用出栈))元素即可
链式栈:返回头结点的数据即可
(5)出栈和返回栈顶元素总是分开的,毕竟有些时候是不需要出栈的(例如检测栈顶元素),但有时候为了方便也可以在多做一个top_pop();
调用上述两个函数就好(保存一个中间变量用于返回)
二顺序栈的c实现
//顺序栈
#include<stdio.h>
#include<stdlib.h>
typedef int ElementType;
const unsigned int size = 100; //预留的size大小
typedef struct stack
{
int msize; //容量
int top_pos; //顶的下标
ElementType *Top; //数组栈
} Stack;
int IsEmpty( Stack *stack ) //判断栈是否为空
{
return stack->top_pos == -1; //即判断顶的下标是否为-1;
}
int IsFull( Stack *stack ) //判断栈是否满了
{
return stack->top_pos == stack->msize - 1; //即判断顶的下标是否为 msize-1;
}
Stack *CreatStack(unsigned int maxsize) //创建一个空栈
{
Stack *stack = malloc(sizeof(Stack));
if (stack == NULL) //查看是否有内存
printf("Out of space!!!\n");
stack->Top = malloc(sizeof( ElementType) * maxsize);
if (stack->Top == NULL) //查看是否有内存
printf("Out of space!!!\n");
stack->msize = maxsize; //输入容量
stack->top_pos = -1; //顶下标为-1 制造空栈
return stack;
}
void push(Stack *stack, ElementType value) //压如元素进栈
{
if (IsFull(stack)) //满栈不能再添加元素了
printf("Full stack!!!");
stack->Top[++stack->top_pos] = value; //顶下标 自增1并写入顶元素
}
ElementType top(Stack *stack) //返回顶元素
{
if (!IsEmpty(stack)) //不能为空栈
return stack->Top[stack->top_pos];
printf("Empty stack!!!");
return 0;
}
void pop(Stack *stack) //顶元素出栈
{
if (!IsEmpty(stack)) //不能为空
{
--stack->top_pos; //top下标自减-即可
return;
}
printf("Empty stack!!!");
}
ElementType top_pop(Stack *stack) //输出出栈的顶元素
{
if (!IsEmpty(stack)) {
ElementType temp = top(stack);
pop(stack);
return temp;
}
printf("Empty stack!!!");
return 0;
}
void MakeEmpty(Stack *stack) //将栈“制空”
{
stack->top_pos = -1;
}
void DisposeStack( Stack *stack ) //删掉整个栈
{
if ( stack != NULL )
{
free( stack->Top );
free( stack );
}
}
int main()
{
Stack *stack = CreatStack(size);
int i = 0;
for (; i < 101; ++i)
{
push(stack, i);
}
for (i = 0; i < 100; ++i) {
printf("%d ", top_pop(stack));
}
printf("\n");
MakeEmpty(stack);
for (i = 101; i < 110; ++i)
{
push(stack, i);
}
for (i = 0; i < 10; ++i) {
printf("%d ", top_pop(stack));
}
return 0;
}
三.链式栈的c++实现
//链式栈
#include<iostream>
using namespace std;
template <class T>
class Link
{
public:
T data; //用于保存结点元素的内容
Link<T> *next; //指向后继结点的指针
Link(const T info, Link<T> *nextValue = nullptr): data(info), next(nextValue) { }
Link(Link<T> *nextValue = nullptr ): next(nextValue) {}
};
template <class T>
class Stack
{
public:
Stack(): Top(nullptr) {} //Top需要保存元素,先制空
bool IsEmpty() { // 栈是否为空
return Top == nullptr;
}
T top() { //返回栈顶元素
if (IsEmpty()) { //空栈没有顶
cout << "Error,this is a blank stack!";
return T(0);
}
else
return Top->data; //返回栈顶元素
}
bool pop() { //栈顶元素出栈
if (IsEmpty()) { //空栈没有顶
cout << "Error,this is a blank stack!";
return false;
}
else { //像链表删除一样的操作,只不过 这里一直删除头结点(Top)
auto temp = Top;
Top = Top->next; //把头结点从栈中去除
delete temp; //释放头结点的空间
return true;
}
}
bool push(T value) { //把元素压进栈中
Top = new Link<T>(value, Top); //头插法连一个链表,相当于一直在top前面接链条
//top一直后退,达到后进先出的效果
return true;
}//头插法插入一个元素,也就是压进栈中
T top_pop() { // 返回出栈了的元素
if (!IsEmpty()) //对非空栈仍然进行操作
{
auto temp = top();
pop();
return temp;
}
cout << "Error,this is a blank stack!" ;
return T(0);
}
void MakeEmpty() { //将整个栈制空,其实也就是析构掉栈
while (!IsEmpty()) {
auto temp = Top; //保存一下要删掉的结点
Top = temp->next;
delete temp;
}
}
~Stack() {
MakeEmpty();
}
private:
Link<T> *Top; //链表栈
};
int main()
{
Stack<int> stack;
for (int i = 0; i < 1000; ++i) {
stack.push(i);
}
for (int i = 0; i < 1000; ++i) {
cout << stack.top_pop() << " ";
}
cout << endl;
stack.MakeEmpty();
if (stack.top_pop())
cout << stack.top_pop();
for (int i = 222; i < 232; ++i) {
stack.push(i);
}
cout << endl;
for (int i = 0; i < 10; ++i) {
cout << stack.top_pop() << " ";
}
return 0;
}
四.c++上应用stack实现后缀表达式的计算
要解决这个问题得先了解什么是后缀表达式****以及c++中stack怎么用
什么是后缀表达式?
(1)•后缀表达式是类似于
4 x * 2 x * a + * c
例子:10 2 / 3 + 5 * 6 == 34== (10/2+3) * 5-6
这种运算符运算符在后面
** 完全不需要括号的计算式**
c++中的stack: stack默认由deque实现,但也可自行修改,主要操作和我们写的是类似的,但是没有清空栈的操作,可以自己写一个(全部pop)
具体实现:
#include<iostream>
using namespace std;
#include<stack> //链式栈
class Calculate
{
public:
Calculate(void) {} //开辟一个空栈
void Run(void); //计算器启动函数
private:
stack<double> stack; //c++上的stack 默认deque实现,也可以改成list实现
//stack<double,list<double>> stack;
bool two_pop( double &oper1, double &oper2); //弹出两个要操作的数
void compulate(char oper); //计算两个数的值并压进栈
double top_pop() { //栈顶元素出栈,并返回其值
double temp = stack.top();
stack.pop();
return temp;
}
void MakeEmpty() { //把栈制空,以备下次使用
while (!stack.empty())
stack.pop();
}
};
bool Calculate::two_pop( double &oper_front, double &oper_back)//弹出两个要操作的数
{
if (stack.empty()) { //先考虑特殊情况
cerr << "error,Missing operand!" << endl;
return false;
}
oper_back = top_pop(); //操作数的后者先出栈(后进先出)
if (stack.empty()) {
cerr << "error,Missing operand!" << endl;
return false;
}
oper_front = top_pop(); //操作数前者出栈
return true;
}
void Calculate:: compulate(char oper) //计算两个数的值并压进栈
{
double oper_front, oper_back; //操作数 前者和后者
bool flag = two_pop(oper_front, oper_back); //弹出两个操作数
if (flag) {
switch (oper) { //符号转换为运算 并把值压进栈
case '+': stack.push(oper_front + oper_back); break;
case '-': stack.push(oper_front - oper_back); break;
case '*': stack.push(oper_front * oper_back); break;
case '/': if (oper_back == 0) {
MakeEmpty(); //stack制空
cerr << "The divisor cannot be ";//除数不能为0
} else {
stack.push(oper_front / oper_back);
}
break;
}
}
else {
MakeEmpty();//没有两个操作数,所以输入有些问题 制空
}
}
void Calculate::Run(void ) //计算器启动函数
{
char c;
double value;
while (cin >> c, c != '=') { //采用cin不接受空格的特点分开读取,并遇到'='结束
switch (c) {
case '+': case '-': case '*': case '/': compulate(c); break;
default:
cin.putback(c); //cin.putback(c)是将字符c放回到输入流中
cin >> value; //这样再 用double的 value cin>>value 可以读完整的数字。
stack.push(value); //数字进栈
break;
}
}
if (!stack.empty()) { //输出栈中的最后结果
cout << "The result of this calclate is: " << top_pop() << endl;
}
}
int main()
{
Calculate c;
c.Run();// 10 2 / 3 + 5 * 6 - 例如输出此式得到34==(10/2+3)*5-6
return 0;
}