【数据结构】03栈与队列-栈

1. 栈

栈(Stack)是限定仅在表尾进行插入和删除操作的线性表
把允许插入和删除的一端称为
栈顶(top)
,另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。栈又称为后进先出(Last in First Out,LIFO)的线性表,简称LIFO结构
栈的插入操作,叫做进栈,压栈或入栈
栈的删除操作,叫做出栈,弹栈*。

2. 顺序栈

栈是线性表的特例,栈的顺序存储结构,也称为顺序栈

2.1 顺序栈的结构定义

#define MAXSIZE 20
typedef int ElemType; // 数据类型
struct SqStack
{
	ElemType data[MAXSIZE]; // 存放数据 
	int top; // 栈顶指针 -1表示空栈 最大值为MAXSIZE-1
};

2.2 入栈操作

bool Push(SqStack* S,ElemType e)
{
	if(S==nullptr||S->top==MAXSIZE-1) // 栈不存在或栈满
	{
		return false;
	}
	// 入栈
	// 1.栈顶指针后移
	S->top++// 2.元素入栈
	S->data[S->top]=e;
	return true;
}

2.3 出栈操作

bool Pop(SqStack* S,ElemType *e)
{
	if(S==nullptr||S->top==-1) // 栈不存在或栈为空
	{
		return false;
	}
	// 保存栈顶元素
	*e = S->data[S->top];
	// 删除元素
	S->top--;
	return true;
}

3.链栈

栈有顺序存储结构,也有链式存储结构,简称为链栈

3.1 链栈结构

struct StackNode
{
	ElemType data; // 数据域
	StackNode* next; // 指针域
};
typedef StackNode* StackPtr; // 栈顶指针
struck LinkStack // 链栈
{
	StackPtr top; // 栈顶指针
	int count; // 栈元素个数
};

3.2 进栈操作

bool Push(LinkStack* S,ElemType e)
{
	if(S==nullptr) // 栈不存在
	{
		return false;
	}
	// 插入元素
	// 1. 准备结点
	StackNode* new_node = new StackNode;
	new_node->data = e;
	// 2. 插入结点
	new_node->next = S->top;
	S->top = new_node;
	// 维护大小
	S->count++;
	return true;
}

3.3 出栈操作

bool Pop(LinkStack*S, ElemType *e)
{
	if(S==nullptr||S->top==nullptr) // 栈不存在或栈为空
	{
		return false;
	}
	// 弹出栈顶元素
	// 1. 存储栈顶元素
	StackNode* top_node = S->top;
	*e = top_node->data;
	// 2. 弹出
	S->top=top_node->next;
	delete top_node;
	return true;
	// 维护长度
	S->count--;
	return true;
}

4. 栈的应用

4.1 递归:斐波那契(Fibonacci)数列的实现

斐波那契数列的表达式为:
F ( n ) = { 0 n = 0 1 n = 1 F ( n − 1 ) + F ( n − 2 ) n > 1 F\left(n\right)=\left\{ \begin{matrix} 0&n=0\\ 1&n=1\\ F\left(n-1\right)+F\left(n-2\right)&n>1 \end{matrix} \right. F(n)= 01F(n1)+F(n2)n=0n=1n>1
利用递归的思想实现斐波那契数列

// 计算Fibonacci数列的第n项
int Fibonacci(int n)
{
	if (n < 2)
		return n == 0 ? 0 : 1;
	return Fibonacci(n - 1) + Fibonacci(n - 2);
}

int main(void)
{
	int a[40];
	for(int i = 0;i<40;i++)
	{
		a[i] = Fibonacci(i);
		cout<<a[i]<<" ";
	}
	cout<<endl;
	return 0;
}

4.1.1 递归的定义

把一个直接调用自己或通过一系列操作间接调用自己的函数,称作递归函数。每个递归必须至少有一个条件,满足时递归不再进行,即不再调用自身而是返回值推出
递归和栈的关系:在前行阶段,随着对每一层的递归,函数的局部变量,参数值和返回地址都被压入栈中。在退回阶段,位于栈顶的局部变量、参数值和返回地址被弹出,用于返回调用层中执行代码其余的部分。

4.2 四则运算表达式求值

4.2.1 后缀表示法

一种不需要括号的后缀表示法,也称为逆波兰(RPN)表示。例如对于公式
9 + ( 3 − 1 ) × 3 + 8 ÷ 2 9+(3-1)\times3+8\div2 9+(31)×3+8÷2的后缀表达式为: 9   3   1 − 3 ×   +   8   2 ÷ + 9\:3\:1-3\times\:+\:8\:2\div + 9313×+82÷+叫做后缀表达式的原因在于所有的符号都是要在运算数字后面出现
我们平时所用的标准四则运算表达式即 9 + ( 3 − 1 ) × 3 + 8 ÷ 2 9+(3-1)\times3+8\div2 9+(31)×3+8÷2叫做中缀表达式,因为所有的运算符号都在两个数字的中间。如何实现中缀表达式转后缀表达式呢?
规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级不高于栈顶符号(乘除优先于加减)则栈顶元素出栈并输出,并将当前符号进栈,直到输出整个表达式。

  • 初始化空栈,用来对符号进出栈使用。
  • 第1个字符是9,输出9,后面是符号 + + +,进栈。栈中元素: + + +
  • 第3个字符是 ( \left(\right. (,进栈。栈中元素: ( , + \left(\right.,+ (,+
  • 第4个字符是3,输出3,后面的符号是 − - ,进栈。栈中元素: − , ( , + -,\left(\right.,+ ,(,+
  • 第6个字符是1,输出1,后面的符号是右括号。此时需要匹配左括号,栈中元素依次输出,直到左括号出栈。输出-。栈中元素: + + +
  • 第8个字符是 × \times ×,进栈。栈中元素: × , + \times,+ ×,+
  • 第9个字符是3,输出3。后面的符号是 + + +,不高于栈顶元素 × \times ×,栈顶元素出栈并输出 × \times × + + +不高于栈顶元素 + + +,栈顶元素出栈,输出+。字符入栈。栈中元素: + + +
  • 第11个字符是8,输出8。后面的符号是 ÷ \div ÷,优先级高于栈顶元素,输出 ÷ \div ÷
  • 第12个字符是2,输出2。栈不为空,输出栈中元素 + + +
    此时输出的表达式为: 9   3   1 − 3 ×   +   8   2 ÷ + 9\:3\:1-3\times\:+\:8\:2\div + 9313×+82÷+

具体实现为:

#include <iostream>
#include <stack>

using namespace std;

// 判断是否为括号
bool isParenthesis(const char c)
{
    if (c == '(' || c == ')')
        return true;
    return false;
}

// 比较两个符号的优先级
int getPri(char c)
{
    switch (c) {
    case '+':
    case '-':
        return 0;   // 如果是加减,返回0
        break;
    case '*':
    case '/':
        return 1;   // 如果是乘除,返回1
        break;
    case '(':
    case ')':
        return -1;  // 这里将括号设为最低优先级,因此括号不会被弹出,除非遇到右括号
        break;
    default:
        break;
    }
}


// 中缀表达式转后缀表达式
string Infix2Postfix(const string& input)
{
	std::stack<char> Stack; // 定义栈
    string output;
    for (int i = 0; i < input.size(); i++)
    {
        if (input[i] >= '0' && input[i] <= '9') // 数字,直接输出
        {
            output.push_back(input[i]);
            output.push_back(' ');
            continue;
        }
        // 判断是否为括号
        if (isParenthesis(input[i]))
        {
            // 是左括号入栈
            if (input[i]=='(')
            {
                Stack.push(input[i]);
            }
            else // 是右括号,弹栈并输出,直到弹栈出左括号
            {
                char out = NULL;
                while (Stack.top()!='(')
                {
                    output.push_back(Stack.top());
                    output.push_back(' ');
                    Stack.pop();
                }
                // 弹出左括号
                Stack.pop();
            }
        }
        // 不是数字也不是括号,是运算符
        else
        {
            // 比较当前符号与栈顶元素的优先级
            // 高于栈顶元素,入栈
            if (Stack.empty()||getPri(input[i]) > getPri(Stack.top()))
            {
                Stack.push(input[i]);
            }
            else // 不高于栈顶元素,将栈顶元素依次出栈,并输出,当前元素入栈
            {
                while (!Stack.empty() && getPri(input[i]) <= getPri(Stack.top()))
                {
                    output.push_back(Stack.top()); // 栈顶元素输出
                    output.push_back(' ');

                    Stack.pop(); // 出栈
                }
                // 元素入栈
                Stack.push(input[i]);
            }
        }
    }
    while (!Stack.empty()) // 将栈中剩余元素输出
    {
        output.push_back(Stack.top());
        output.push_back(' ');

        Stack.pop();
    }
    return output;
}
int main(void)
{
    string input;
    cout << "输入运算表达式:";
    cin >> input;
    string out=Infix2Postfix(input);
    cout << out << endl;
    return 0;
}
  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值