【数据结构】栈和队列的应用

news2024/11/16 9:38:25

🎇[数据结构]栈和队列的应用🎇


在这里插入图片描述


🌟 正式开始学习数据结构啦~此专栏作为学习过程中的记录🌟


文章目录

  • 🎇[数据结构]栈和队列的应用🎇
  • 🍰一.栈在括号表达式中的应用
    • 🚀1.原理
    • 🚀2.代码实现
      • 🔆1.初始化
      • 🔆2.入栈
      • 🔆3.出栈
      • 🔆4.判断括号是否匹配
  • 🍰二.栈在递归中的应用
    • 🚀1.原理
    • 🚀2.栈与递归
  • 🍰三.栈在表达式中的应用
    • 🚀1.中缀转后缀表达式
      • 🔆1.原理
      • 🔆2.实现
    • 🚀2.后缀表达式的计算
  • 🍰四.队列在层序遍历中的应用
  • 🍰五.队列在计算机系统中的应用


🍰一.栈在括号表达式中的应用

🚀1.原理

对于编译器来说,我们在大多数 I D E IDE IDE 内进行编码时,都会提示括号的匹配标志,可能用不同颜色或者距离差加以区分,那么,编译器中是如何实现这些操作的呢?

在这里插入图片描述

其实思路很简单,我们可以用 来完成:

考虑以下括号序列:

在这里插入图片描述

单身数组:视左括号为男嘉宾,右括号为女嘉宾,男嘉宾会有一个愿望,如果当前女嘉宾与男嘉宾的愿望一致,则牵手成功,男嘉宾就会离开单身数组


活动内容:
愿望 ① ① : 当计算机接收到了左括号 1 1 1,它希望有一个右括号 8 8 8与之匹配
愿望 ② ② : 再获得左括号 2 2 2,其希望有一个右括号 7 7 7与之对应
愿望 ③ ③ : 再是左括号 3 3 3,希望有一个右括号 4 4 4与之匹配
此时,单身数组内存入了左括号: 1   2   3 1\ 2\ 3 1 2 3

在这里插入图片描述

于是,再接收下一个括号 4 4 4,发现可以和此时数组中最后一个元素括号 3 3 3 进行匹配,于是将括号 3 , 4 3,4 34 完成匹配,离开了单身数组,并将愿望 ③ ③ 移除愿望清单中

在这里插入图片描述

继续遍历括号,接收到左括号 5 5 5,加入数组,此时左括号 5 5 5 希望有一个右括号 6 6 6 与之匹配 (愿望 ③ ③ )

此时,接收下一个括号为右括号 6 6 6,发现可以和数组中最后一个元素括号 5 5 5 进行匹配,于是 5 5 5 6 6 6完成配对,离开单身数组

在这里插入图片描述

之后分别为右括号 7 , 8 7,8 78,分别满足了愿望 ② ② 和愿望 ① ① ,因此,所有男女嘉宾全部配对成功(栈空)

在这里插入图片描述

不难发现,最后一个愿望往往会最先被实现,即最后加入的左括号会最先进行匹配,满足先进后出:栈 ( L I F O ) (LIFO) LIFO


算法实现:

  1. 构造一个空栈用于存放左括号
  2. 不断加入左端括号,直到遇到右端括号时停止,进行匹配
  3. 不断重复,直到所有的左右括号全部匹配,则成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YPmNcxpJ-1687505993011)(2023-04-19-15-45-27.png)]


匹配不成功的情况:

1.当前扫描到的左右括号不匹配
在这里插入图片描述


2.右括号为单身括号

存在还没有匹配的右括号

在这里插入图片描述


3.左括号为单身括号

存在还有没有匹配的左括号

在这里插入图片描述


🚀2.代码实现

🔆1.初始化

void InitStack(SqStack& S) {
    S.top = -1;//初始化栈顶指针
}

🔆2.入栈

bool Push(SqStack& S, Elemtype x) {
    if (S.top == Maxsize - 1) //栈满
        return false;
    S.data[++S.top] = x;
    return true;
}

🔆3.出栈

bool Pop(SqStack& S, Elemtype& x) {
    if (S.top == -1)
        return false;
    x = S.data[S.top--];
    return true;
}

🔆4.判断括号是否匹配

step:

  1. 遍历给定的括号字符型数组
  2. 如果为左括号,则加入栈中
  3. 如果为右括号,先将当前栈中的栈顶元素出栈,与之匹配
  4. 当所有括号遍历完,且栈为空,则匹配成功

代码实现:

bool bracketcheck(char str[], int length) {
    SqStack S;
    InitStack(S); //初始化一个栈
    for (int i = 0; i < length; i++) {
        if (str[i] == '(' || str[i] == '[' || str[i] == '{') {
            Push(S, str[i]);
        }
        else {
            if (isEmpty(S)) //遇到右括号,且当前栈为空
                return false;

            Elemtype topElem; //接收栈顶元素
            Pop(S, topElem); //弹出栈顶元素
            if (str[i] == ')' && topElem != '(')
                return false;
            if (str[i] == ']' && topElem != '[')
                return false;
            if (str[i] == '}' && topElem != '{')
                return false;
            }
        }
    return isEmpty(S); //如果全部匹配,且栈空,则成功
}

完整代码实现:

#include<iostream>
#define Maxsize 10
#define Elemtype char
using namespace std;


typedef struct {
    Elemtype data[Maxsize]; //静态数组存放栈中元素
    int top; //定义栈顶指针
}SqStack;


void InitStack(SqStack& S) {
    S.top = -1;//初始化栈顶指针
}

bool Push(SqStack& S, Elemtype x) {
    if (S.top == Maxsize - 1) //栈满
        return false;
    S.data[++S.top] = x;
    return true;
}

bool Pop(SqStack& S, Elemtype& x) {
    if (S.top == -1)
        return false;
    x = S.data[S.top--];
    return true;
}

bool isEmpty(SqStack S) {
    if (S.top == -1)
        return true;
    return false;
}


bool bracketcheck(char str[], int length) {
    SqStack S;
    InitStack(S); //初始化一个栈
    for (int i = 0; i < length; i++) {
        if (str[i] == '(' || str[i] == '[' || str[i] == '{') {
            Push(S, str[i]);
        }
        else {
            if (isEmpty(S)) //遇到右括号,且当前栈为空
                return false;

            Elemtype topElem; //接收栈顶元素
            Pop(S, topElem); //弹出栈顶元素
            if (str[i] == ')' && topElem != '(')
                return false;
            if (str[i] == ']' && topElem != '[')
                return false;
            if (str[i] == '}' && topElem != '{')
                return false;
        }
    }
    return isEmpty(S); //如果全部匹配,且栈空,则成功
}



int main() {
    int SampleNum;
    char str[Maxsize];
    cout << "请输入括号的个数:" << endl;
    cin >> SampleNum;
    cout << "请输入待匹配的括号:" << endl;
    cin >> str;

    if (bracketcheck(str, SampleNum))
        cout << "匹配成功" << endl;
    else
        cout << "匹配失败" << endl;

    system("pause");
    return 0;
}

输出结果:

在这里插入图片描述



🍰二.栈在递归中的应用

🚀1.原理

对于递归,相信大家已经不陌生了,简单来说,就是函数在中调用自身的过程,就是递归

它通常用于将大规模的复杂问题一层层地转化为小问题,即不断降低问题规模,但是一种追求代码的简洁性而牺牲运算效率的算法


实现递归所需的空间复杂度很高,当我们使用递归函数时,计算机内部会为我们开辟一个函数调用栈,递归到的每一层都会存入这个栈中,用于记录当前层变量的状态,由此,当问题规模很大时,往往需要开辟一大片连续的内存空间,所以,递归的适用条件为:

①可以把原始问题转化为属性相同的小问题; ②问题规模较小


所以,我们可以理解为,递归的本质就是栈

在这里插入图片描述


🚀2.栈与递归

如下,为递归的应用场景:

在这里插入图片描述


在这里插入图片描述

由此总结出,递归的缺陷为:

① ① 空间要求过大,容易溢出; ② ② 在递归过程中会有许多重复的计算,导致时间开销过大


可以看到,对于这两道典型的递归函数例题,其实都需要栈,我们在平时写的递归中不容易发现,是因为计算机在内部为我们实现了栈的功能

所以,我们尝试主动模拟该过程,即通过构建栈来实现


e . g e.g e.g 以斐波那契数列为例:

其定义为:

在这里插入图片描述

1.递归实现:

//斐波那契数列的递归实现
int fib(int n) {
    if (n == 0)
        return 0;
    else if (n == 1)
        return 1;
    else
        return fib(n - 1) + fib(n - 2);
}

2.栈实现:

🔱思路分析:

其实要用栈实现,我们只需要模拟其入栈和出栈的过程

🎯 因为数列中的每一项都是由前面两个项所得来的,所以我们可以构造斐波那契数列的二叉树

最后加到结果上的其实都是叶子结点,这里我们定义叶子结点为 n = 1 或 n = 2 n=1 或 n=2 n=1n=2 时,因为: f i b ( 1 ) = f i b ( 2 ) = 1 fib(1)=fib(2)=1 fib(1)=fib(2)=1

在这里插入图片描述

step:

  1. 构造一个空栈,并将根结点传入
  2. 每一轮都弹出栈顶元素,判断是否为叶子结点 ( n = 1 ∣ ∣ n = 2 ) (n=1 || n=2) (n=1∣∣n=2)
  3. ① ① 若弹出的为叶子结点,则结果 r e s + = 1 res+=1 res+=1 (因为叶子节点的值只有 1 1 1);
    ② ② 若弹出的不是叶子结点,则将该结点的两个子结点压入栈中;
  4. 重复 ②,③ ②,③ 操作,直到栈为空,返回结果

递归栈:

在这里插入图片描述


功能函数实现:

//斐波那契数列的栈实现
int fib_Stack(int n) { //求解fib(n)的值
	Stack S;
	InitStack(S);
	int res = 0;
	Push(S, n);
	while (!Stackempty(S)) //当栈不为空
	{
		int x = Pop(S);
		if (x == 1 || x == 2) { //叶子结点
			res += 1; //因为fib(1)=fib(2)=1
		}
		else //此时还不能加,则将两个子节点压入栈
		{
			Push(S, x - 1);
			Push(S, x - 2);
		}
	}
	return res;
}

完整代码实现:

#include<iostream>
#define Maxsize 50
using namespace std;

typedef struct {
	int data[Maxsize];
	int top;
}Stack;

//初始化
void InitStack(Stack& S) {
	S.top = -1;
}

//判空
bool Stackempty(Stack& S) {
	if (S.top == -1)
		return true;
	return false;
}

//判满
bool Stackover(Stack& S) {
	if (S.top >= Maxsize)
		return true;
	return false;
}

//出栈
int Pop(Stack& S) {
	if (Stackempty(S)) //为空
	{
		cout<<"栈空" << endl;
		return '\0';
	}
	return S.data[S.top--];

}

//入栈
bool Push(Stack& S, int x) {
	if (Stackover(S))
		return false;
	S.data[++S.top] = x;
	return true;
}

//斐波那契数列的递归实现
int fib(int n) {
	if (n == 0)
		return 0;
	else if (n == 1)
		return 1;
	else
		return fib(n - 1) + fib(n - 2);
}

//斐波那契数列的栈实现
int fib_Stack(int n) { //求解fib(n)的值
	Stack S;
	InitStack(S);
	int res = 0;
	Push(S, n);
	while (!Stackempty(S)) //当栈不为空
	{
		int x = Pop(S);
		if (x == 1 || x == 2) { //叶子结点
			res += 1; //因为fib(1)=fib(2)=1
		}
		else //此时还不能加,则将两个子节点压入栈
		{
			Push(S, x - 1);
			Push(S, x - 2);
		}
	}
	return res;
}

int main() {
	int n;
	cout << "请输入要求解的fib数列的项数(>=1):" << endl;
	cin >> n;

	cout << "递归求解结果为:" << fib(n) << endl;
	cout << "栈求解结果为:" << fib_Stack(n) << endl;

	system("pause");
	return 0;
}


输出结果:
在这里插入图片描述



🍰三.栈在表达式中的应用

🚀1.中缀转后缀表达式

🔆1.原理

表达式求值是程序设计语言中一个最基本的问题,对于现实生活中一般的计算式,我们可以快速得出结果,但计算机处理却十分麻烦,因为存在计算符号之间的优先级,因此,我们需要一种特殊的方法,可以不用括号,也能得到结果,我们基于来实现


那这和栈有什么关系呢?让我们继续探究
在这里插入图片描述


1.中缀表达式运算符在两个操作数中间

这是我们最熟悉最常用的表达式:

在这里插入图片描述


不难发现,对于中缀表达式而言,加括号与不加括号的结果是不一样的,计算机在处理这些括号的时候很糟心,因为它还需要找到与之相匹配的括号,再检查内部是否还有括号,当数据量足够大时,程序难以进行


那么,我们就需要一个既不用括号,又可以完整地表示出正确含义的表达式

在这里插入图片描述

改进:

1.后缀表达式运算符在两个操作数后面
2.前缀表达式运算符在两个操作数前面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BdgFFwJV-1687505993015)(2023-04-21-10-19-13.png)]


🔆2.实现

一.中缀转后缀表达式:(手算实现)

运算规则:

  1. 确定中缀表达式中各个运算符的运算顺序
  2. 选择下一个运算符,按照 [ [ [左操作数 右操作数 运算符 ] ] ] 的方式进行组合,成为一个新的表达式
  3. 重复 ② ② ,直到运算符全部被处理

step

我们可以看到,当确定完优先级之后,我们从优先级最高的开始计算:

  1. 找到 ①,此时左右操作数为 1 1 1 1 1 1,因此,我们按照构造规则改为 11 + 11+ 11+ ,视为一个整体:
    ( ( 15 / ( 7 − ( 11 + ) ) ∗ 3 ) − ( 2 + ( 1 + 1 ) ) ((15 / (7-(11+))*3)-(2+(1+1)) ((15/(7(11+))3)(2+(1+1))

  2. 再找到 ②,此时左右操作数为 7 7 7 和 $11+ $,因此,我们改为 7 ( 11 + ) − 7(11+)- 7(11+)
    ( ( 15 / 7 ( 11 + ) − ) ∗ 3 ) − ( 2 + ( 1 + 1 ) ) ((15/7(11+)-)*3)-(2+(1+1)) ((15/7(11+))3)(2+(1+1))

  3. 再找到 ③,此时左右操作数变为了 15 15 15 711 + − 711+- 711+,同样地,我们得到:
    ( ( 15 ( 711 + − ) / ) ∗ 3 ) − ( 2 + ( 1 + 1 ) ) ((15(711+-)/)*3)-(2+(1+1)) ((15(711+)/)3)(2+(1+1))
  4. 以此类推,我们就可以得到上述的最终表达式

后缀表达式的关键:不断合体为一个操作数


注意:

但是其实,后缀表达式并不唯一,这里有两个括号,如果我们先计算第二个括号内的数,再计算第一个括号内的数,最后加起来,其实结果是一样的

由此,我们需要有条件去约束它:


后缀表达式中的左优先原则:

只要左边的运算符能先计算,就优先计算左边的,这样就确保了表达式的唯一性

在这里插入图片描述


也就是中缀表达式中运算符的优先符的计算顺序与后缀表达式中运算符从左往右的出现顺序相同

在这里插入图片描述


前缀表达式中的右优先原则:

对于前缀表达式,是从右往左来反映运算符的优先级的,其余和后缀表达式类似,就不再赘述了
在这里插入图片描述


我们通过上述方法,已经解决了手算问题,那么我们现在思考,这一过程在计算机上该如何实行呢?


二.中缀转后缀表达式:(机算实现)

准备:

转为后缀表达式时,需要根据操作符 o p op op 的优先级来进行栈的动态变化,我们用 i c p icp icp[栈内优先数]来表示当前扫描到运算符未进栈时的优先级,该 运算符进栈后的优先级 i s p isp isp[栈外优先数]

操作符*,/+,-
i s p isp isp1536
i c p icp icp6421

🎯 运算规则:(基于只有加减乘除)

  1. 初始化一个栈,用于保存 运算符

  2. 从左向右处理中缀表达式中的各个元素,直到末尾

  3. 在处理各个元素时,会遇到以下情况:
    a. 遇到 操作数,直接加入后缀表达式字符串 b e h i n d behind behind


    b. 遇到 界限符→括号

    遇到左括号 ′ ( ′ '(' (,直接入栈;
    遇到右括号 ′ ) ′ ')' ),不断将操作符弹出,加入后缀表达式,直到遇到 ‘(’,结束;

    注意:弹出的括号不加入表达式


    c. 遇到 运算符
    依次弹出栈内优先级高于或等于当前运算符的所有运算符,加入后缀表达式,若遇到 ′ ( ′ '(' ( 或栈空则停止,最后将当前运算符加入栈中;

  4. 在处理完所有元素后,依次弹出栈内剩余元素,加入后缀表达式 b e h i n d behind behind


🍰 e . g e.g e.g 对于表达式: ( A − B ) ∗ C − D (A-B)*C-D (AB)CD

遍历中缀表达式,第一个是运算符 ( ( (,直接压入栈中,第二个是操作符 A A A,直接加入后缀表达式,第三个是运算符 − - ,因为 i c p ( − ) > i s p ( ( ) icp(-)>isp(() icp()>isp((),也就是优先级比 ( ( ( 高,所以直接压入栈中,再在后缀表达式中加入操作符 B B B,此时, b e h i n d = A B behind=AB behind=AB
在这里插入图片描述


下一个是运算符 ) ) ),于是,不断弹出栈中元素,直到遇到 ( ( (,将除了括号外的其他运算符依次加到表达式后面,此时, b e h i n d = A B − behind=AB- behind=AB
在这里插入图片描述


之后,将运算符 ∗ * 入栈, b e h i n d behind behind 后加入 C C C,当遇到 − - 时,由于减号的优先级小于乘号,所以,弹出 ∗ * ,并将 − - 入栈,此时, b e h i n d = A B − C ∗ behind=AB-C* behind=ABC

在这里插入图片描述


最后,加入中缀表达式中的最后一个运算符 D D D,再将栈内元素全部弹出,加入到 b e h i n d behind behind,最终表达式为 : b e h i n d = A B − C ∗ D − behind=AB-C*D- behind=ABCD


功能函数实现:

//功能函数
string change(string middle) {
    Stack S;
    InitStack(S);
    string behind = "";
    char op;//运算符

    //遍历中序表达式中的所有元素
    for (int i = 0; i < middle.length(); i++)
    {
        char x = middle[i];

        //1.数字或其他
        if (!isOp(x))
            behind += middle[i];

        //2.左括号
        else if (x == '(') //左括号进入时优先级最高,直接压入栈
            Push(S, x);

        //3.右括号
        else if (x == ')') //右括号进入时优先级低,一直出栈直到遇到左括号
        {
            while (!Stackempty(S)) {
                op = Pop(S);
                if (op == '(') //如果为右括号则停下
                    break;
                else
                    behind += op; //否则一直弹出运算符
            }
        }

        //4.运算符
        else
        {
            //4.1运算符为乘除
            if (x == '*' || x == '/')
            {
                while (!Stackempty(S)) {
                    op = Pop(S); //将运算符弹出栈
                    if (op == '(')
                        break;
                    else
                    {
                        if (op == '+' || op == '-') //如果为加或减(优先级小)则重新将他们压回去
                        {
                            Push(S, op);
                            break; //不要忘了break,不然就死循环了,一直出栈入栈...
                        }
                        else  //遇到乘或除
                            behind += op; //加入该弹出运算符
                    }
                }
            }

            //4.2运算符为加减
            else if (x == '+' || x == '-')
            {
                while (!Stackempty(S)) {
                    op = Pop(S);
                    if (op == '(')
                        break;
                    else
                    {
                        behind += op;
                    //因为加减不管遇到加减还是乘除,都不能满足优先级大,所以都要弹出并加入后缀表达式
                    }
                }
            }

            Push(S, x); //这是包含在情况4中的 表示将当前运算符压入栈
        }
    }

    //当中序表达式中元素全部被便遍历完了之后,弹出栈中所有元素
    while (!Stackempty(S))
        behind += Pop(S);

    return behind;
}


int main() {
    string middle;
    cin >> middle;
    cout << change(middle) << endl;

    system("pause");
    return 0;
}



完整代码实现:

#include<iostream>
#define Maxsize 50
using namespace std;

typedef struct {
	char data[Maxsize]; //栈是存运算符的,所以为字符型
	int top; //栈顶
}Stack;

//初始化
void InitStack(Stack& S) {
	S.top = -1;
}

//栈满
bool Stackover(Stack& S) {
	if (S.top >= Maxsize)
		return true;
	else
		return false;
}

//栈空
bool Stackempty(Stack& S) {
	if (S.top == -1)
		return true;
	else
		return false;
}

//入栈
void Push(Stack& S, char x) { //将x压入栈中
	if (!Stackover(S)) //如果栈不满
		S.data[++S.top] = x;
	else
		cout << "加入" << x << "时栈满" << endl;
}

//出栈
char Pop(Stack& S) {
	if (!Stackempty(S))
		return S.data[S.top --];
	else {
		cout << "弹出失败,栈空" << endl;
		return '\0';
	}
}

//判断是否为运算符
bool isOp(char x) {
	if (x == '(' || x == ')' || x == '+' || x == '-' || x == '*' || x == '/')
		return true;
	else
		return false;
}

//功能函数
string change(string middle) {
    Stack S;
    InitStack(S);
    string behind = "";
    char op;//运算符

    //遍历中序表达式中的所有元素
    for (int i = 0; i < middle.length(); i++)
    {
        char x = middle[i];

        //1.数字或其他
        if (!isOp(x))
            behind += middle[i];

        //2.左括号
        else if (x == '(') //左括号进入时优先级最高,直接压入栈
            Push(S, x);

        //3.右括号
        else if (x == ')') //右括号进入时优先级低,一直出栈直到遇到左括号
        {
            while (!Stackempty(S)) {
                op = Pop(S);
                if (op == '(') //如果为右括号则停下
                    break;
                else
                    behind += op; //否则一直弹出运算符
            }
        }

        //4.运算符
        else
        {
            //4.1运算符为乘除
            if (x == '*' || x == '/')
            {
                while (!Stackempty(S)) {
                    op = Pop(S); //将运算符弹出栈
                    if (op == '(')
                        break;
                    else
                    {
                        if (op == '+' || op == '-') //如果为加或减(优先级小)则重新将他们压回去
                        {
                            Push(S, op);
                            break; //不要忘了break,不然就死循环了,一直出栈入栈...
                        }
                        else  //遇到乘或除
                            behind += op; //加入该弹出运算符
                    }
                }
            }

            //4.2运算符为加减
            else if (x == '+' || x == '-')
            {
                while (!Stackempty(S)) {
                    op = Pop(S);
                    if (op == '(')
                        break;
                    else
                    {
                    behind += op;
                    //因为加减不管遇到加减还是乘除,都不能满足优先级大,所以都要弹出并加入后缀表达式
                    }
                }
            }

            Push(S, x); //这是包含在情况4中的 表示将当前运算符压入栈
        }
    }

    //当中序表达式中元素全部被便遍历完了之后,弹出栈中所有元素
    while (!Stackempty(S))
        behind += Pop(S);

    return behind;
}


int main() {
    string middle;
    cin >> middle;
    cout << change(middle) << endl;

    system("pause");
    return 0;
}

输出结果:

在这里插入图片描述



🚀2.后缀表达式的计算

将中缀表达式转为后缀表达式后,还需要计算后缀表达式,才能得到最终结果

一.后缀表达式的计算:(手算实现)

计算法则:

从左向右扫描,每遇到一个运算符,就让运算符之前最近的两个数运算,运算之后,立即返回结果,合体为一个操作数


在这里插入图片描述


在这里插入图片描述

由此,我们可以得到结论:

对于表达式的转换和计算:

相同点:都要构造一个栈实现

不同点:

  1. 对于表达式的转换而言,我们是构造一个存放运算符的栈,而操作数不会进入栈
  2. 而对于计算而言,我们需要构造一个存储操作符运算结果的栈,也就是本质是存放操作数,而运算符只是用于栈内元素的计算,不会存入栈

所以,转换 → 运算符栈,计算 → 操作符栈 所以,转换→运算符栈 ,计算→操作符栈 所以,转换运算符栈,计算操作符栈



二.后缀表达式的计算:(机算实现)

🔱思路分析:

step:

  1. 从左往右扫描下一个元素,直到处理完所有元素
  2. 若扫描到 操作符,则压入栈,并回到 ①;
  3. 若扫描到 运算符,则弹出两个栈顶元素,执行运算,运算结果压回到栈顶;
    结果 = [ =[ =[后出栈元素 运算符 先出栈元素 ] ] ]

    注意:这里先出栈的是右操作符

  4. 重复执行,直到表达式内元素全部被遍历完

e . g e.g e.g 我们来看如下的表达式

在得到了后缀表达式之后:

在这里插入图片描述

计算图解如下:
在这里插入图片描述

r e s : res: res:

在这里插入图片描述



功能函数代码:

//计算后缀表达式
void Caculate(string behind[],int len)
{
    Stack S; //创建栈,用于保存操作数
    InitStack(S);
    for (int i = 0; i < len; i++)
    {
        //如果是运算数将其压入栈
        if (!isOp(behind[i]))
        {
            Push(S, atoi(behind[i].c_str()));
        }
        //如果是操作符依次弹出两个操作数
        else
        {
            double num1 = Pop(S); //右操作数
            double num2 = Pop(S); //左操作数

            //执行相应运算,将运算结果压入栈中
            if (behind[i] == "+")
            {
                Push(S, num1 + num2);
            }
            else if (behind[i] == "-")
            {
                Push(S, num2 - num1);
            }
            else if (behind[i] == "*")
            {
                Push(S, num1 * num2);
            }
            else if (behind[i] == "/")
            {
                Push(S, num2 / num1);
            }
        }
    }
    //最终栈顶元素即为结果
    cout <<"结果为:" << Pop(S) << endl;
}

完整代码实现:

#include<iostream>
#include<string>
#define Maxsize 50
using namespace std;

typedef struct {
    double data[Maxsize];
    int top; //栈顶
}Stack;

//初始化
void InitStack(Stack& S) {
    S.top = -1;
}

//栈满
bool Stackover(Stack& S) {
    if (S.top >= Maxsize)
        return true;
    else
        return false;
}

//栈空
bool Stackempty(Stack& S) {
    if (S.top == -1)
        return true;
    else
        return false;
}

//入栈
void Push(Stack& S, double x) { //将x压入栈中
    if (!Stackover(S)) //如果栈不满
        S.data[++S.top] = x;
    else
        cout << "加入" << x << "时栈满" << endl;
}

//出栈
double Pop(Stack& S) {
    if (!Stackempty(S))
        return S.data[S.top--];
    else {
        cout << "弹出失败,栈空" << endl;
        return '\0';
    }
}

bool isOp(string x) {
    if (x == "+" || x == "-" || x == "*" || x == "/")
        return true;
    else
        return false;
}

//计算后缀表达式
void Caculate(string behind[],int len)
{
    Stack S; //创建栈,用于保存操作数
    InitStack(S);
    for (int i = 0; i < len; i++)
    {
        //如果是运算数将其压入栈
        if (!isOp(behind[i]))
        {
            Push(S, atoi(behind[i].c_str()));
        }
        //如果是操作符依次弹出两个操作数
        else
        {
            double num1 = Pop(S); //右操作数
            double num2 = Pop(S); //左操作数

            //执行相应运算,将运算结果压入栈中
            if (behind[i] == "+")
            {
                Push(S, num1 + num2);
            }
            else if (behind[i] == "-")
            {
                Push(S, num2 - num1);
            }
            else if (behind[i] == "*")
            {
                Push(S, num1 * num2);
            }
            else if (behind[i] == "/")
            {
                Push(S, num2 / num1);
            }
        }
    }
    //最终栈顶元素即为结果
    cout <<"结果为:" << Pop(S) << endl;
}

int main()
{
    //1.输入后缀表达式
    string behind[Maxsize];
    int len=0;
    string x;
    while (cin >> x) {
        behind[len++] = x;
        if (cin.get() == '\n')
            break;
    }

    //2.计算后缀表达式
    Caculate(behind,len);

    system("pause");
    return 0;
}

输出结果:

在这里插入图片描述



🍰四.队列在层序遍历中的应用

✨ 树是一种非常重要的数据结构,它在计算机科学中的应用非常广泛,我们在学习树的过程中,常常会遇到树的遍历,树的遍历分为三种方式:前序遍历、中序遍历和后序遍历,而在树的层次遍历中,我们需要使用 队列 这种数据结构

树的层次遍历是一种非常重要的遍历方式,它的遍历顺序是从树的根节点开始,一层一层地遍历,直到遍历完所有的节点,在这个过程中,每一层的节点都按照从左到右的顺序进行遍历

图解:

在这里插入图片描述

step:

  1. 根节点入队
  2. 若队空,即所有结点都已经处理完,则结束遍历;
  3. 若队不空,队列中的第一个结点出队,并对其进行访问,令其左右孩子入队(如果没有则跳过)
  4. 不断重复,直到队为空,则得到层序遍历结果

代码实现:

void levelTraverse(TreeNode *root) {
    queue<TreeNode*> q;
    q.push(root);
    while(!q.empty()) {
        TreeNode* node = q.front();
        q.pop();
        cout << node->val << " ";
        if(node->left) q.push(node->left);
        if(node->right) q.push(node->right);
    }
}


🍰五.队列在计算机系统中的应用

✨在目前的计算机技术中,大量的外部设备,例如打印机和扫描仪等设备的工作速度比主机更为缓慢。它们也许需要一段时间来完成它们的工作。这就导致了一个问题,尽管它们接收到来自主机发送的任务,但是它们可能并没有准备好去处理它们。这会导致一些任务被阻塞,系统的处理速度也会变慢。这时,许多计算机系统使用队列来解决这个问题

对于主机与打印机速度不匹配的问题:

在一个计算机系统中,多个用户可能同时请求打印机去工作。这可能导致打印机出现资源竞争的情况,最终导致任务的延迟和其他问题。如果没有一个好的管理机制去控制多个请求的呈现,这些请求可能会让打印机出现超负荷运行。这时,管理员将可以使用队列来管理打印机任务。当有新的打印请求时,管理员会将它们排队,等候被处理。打印机接收每个任务,并从队列中按照优先级或其他条件按顺序地处理任务

对于CPU资源的竞争的问题:

数量庞大的用户同时使用计算机资源时,也会发生类似的竞争情况。这种竞争会极大地影响系统的整体性能和稳定性。当多个用户同时请求计算机资源时,管理员也可以将请求加入到一个队列中,依照前面的例子,以解决这个问题。每个请求会在队列中等候被处理,管理员会按照特定的规则对其进行排序。然后,当计算机资源可用时,管理员可以按照队列中请求的顺序,将它们授予用户访问权限


🎇本节详细讲解了栈和队列的应用场景,有没有对计算机的操作更加了解了呢~🎇

如有错误,欢迎指正~!


在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/677009.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux1.基础指令(上)

1.Linux系统可创建多个用户。 2.创建用户:adduser 用户名 设置密码:passwd 用户名 (系统会提示再次输入密码&#xff0c;注意密码不回显)。 3.删除用户首先要在root权限下&#xff0c;输入指令:userdel -r 用户名。 4.ls指令 ls -a(显示所有文件&#xff0c;包括隐藏文件) :…

【软件设计师暴击考点】计算机组成原理与体系结构高频考点暴击系列【二】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软件…

【P3】kali 最强渗透工具 - metasploit(安装配置及使用教程详解)

文章目录 一、metasploit 是什么&#xff1f;二、metasploit 攻击 windows 操作系统三、metasploit 攻击永恒之蓝全流程四、metasploit 攻击成功后能做什么4.1、操作步骤4.2、攻击示例 五、msfvenom 绕过杀毒软件技巧5.1、捆绑木马5.2、加壳&#xff1a;压缩壳、加密壳5.2.1、T…

分片和一致性哈希

在设计大规模分布式系统时&#xff0c;你可能会遇到两个概念——分片&#xff08;sharding&#xff09;和一致性哈希&#xff08;consistent hashing&#xff09;。虽然我在网上找到了很多关于这些术语的解释&#xff0c;但它们让我感到有些困惑。我觉得分片和一致性哈希本质上…

Web网页制作-知识点(2)——常用文本标签、列表标签、表格标签、Form表单、块元素与行内元素(内联元素)

目录 常用文本标签 列表标签 有序列表 无序列表 定义列表 表格标签 表格组成与特点 表格标签 表格属性 ​​​合并表格单元格 Form表单 属性说明 表单元素 文本框 密码框 提交按钮 块元素与行内元素&#xff08;内联元素&#xff09; 内联元素和块级元素…

Flink JdbcSink.sink源码解析及常见问题

文章目录 源码入口我们看下flush方法干了什么flush方法至此走完了&#xff0c;但是什么时机写入的数据呐&#xff1f;补充总结&#xff1a; 常见问题1. 为什么会出现JdbcSink.sink方法插入Mysql无数据的情况&#xff1f;2. JdbcSink.sink写Phoenix无数据问题 参考 基于Flink 1.…

设计模式之组合模式笔记

设计模式之组合模式笔记 说明Composite(组合)目录组合模式示例类图菜单组件抽象类菜单类菜单项类测试类 说明 记录下学习设计模式-组合模式的写法。JDK使用版本为1.8版本。 Composite(组合) 意图:将对象组合成树型结构以表示“部分-整体”的层次结构。Composite使得用户对单…

Linux网络-网络层IP协议

目录 IP协议 计算机网络分层 IP协议头格式 IP数据报 - 数据分片 数据报为什么要分片&#xff1f; 数据报分片是什么&#xff1f; 如何做到IP数据报分片&#xff1f; 分片demo示例 并不推荐分片&#xff0c;能不分片则不分片。 网段划分 前置了解 网络号和主机号 为…

如何监测和优化阿里云服务器的性能?有哪些性能分析工具和指标?

如何监测和优化阿里云服务器的性能&#xff1f;有哪些性能分析工具和指标&#xff1f;   阿里云服务器性能监测与优化是云计算服务中一个非常重要的环节。为了确保服务器稳定、高效地运行&#xff0c;我们需要对其性能进行监测&#xff0c;并在监测的基础上进行优化。本文将为…

Packet Tracer - 综合技能练习(配置 VLAN、中继、DHCP 服务器、DHCP 中继代理,并将路由器配置为 DHCP 客户端)

Packet Tracer - 综合技能练习 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 R1 G0/0.10 172.31.10.1 255.255.255.224 不适用 G0/0.20 172.31.20.1 255.255.255.240 不适用 G0/0.30 172.31.30.1 255.255.255.128 不适用 G0/0.40 172.31.40.1 255.255…

MySQL权限控制及日志管理

MySQL权限控制及日志管理 用户权限管理 创建用户 CREATE USER 用户名IP地址 [ IDENTIFIED BY 密码 ]&#xff1b;GRANT SELECT ON *.* TO 用户名’IP地址’ IDENTIFIED BY "密码"&#xff1b;--创建一个用户名为Usr1 密码为 Usr1.mysql的用户 并授权 CREATE USER…

无忧行:突破网络封锁、跨境访问国外的网站和应用程序(安装注册及使用教程详解)

文章目录 步骤一&#xff1a;注册微软账号步骤二&#xff1a;修改账号的国家/地区步骤三&#xff1a;在Edge Dev浏览器中安装无忧行插件步骤四&#xff1a;创建 无忧行 账户步骤五&#xff1a;无忧行使用教程 包括注册微软账号、在Edge Dev浏览器中安装无忧行插件、创建 无忧行…

Python基础篇(六):组织管理代码—模块和包

组织管理代码—模块和包 前言模块(Module)创建模块使用模块 包(Package)创建包使用包 前言 在Python中&#xff0c;模块和包是组织和管理代码的重要概念。模块是一个包含 Python 定义和语句的文件&#xff0c;而包则是一组相关模块的目录。它们是组织和管理代码的强大工具&…

【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 文章目录 系列文章目录前言一、所有权(Ownership)1.1.、所有权(Ow…

【unity每日一记】 Camera相机+ Screen屏幕+动画机

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

Flutter开发——图片加载与缓存源码解析

在Flutter中有个图片组件&#xff1a;Image,通常会使用它的Image.network(src)、Image.file(src)、Image.asset(src)来加载图片。 下面是Image的普通构造方法&#xff1a; const Image({super.key,required this.image,this.frameBuilder,this.loadingBuilder,this.errorBuilde…

第四章 机器学习

文章目录 第四章 决策树4.1基本流程4.2划分选择4.2.1信息增益4.2.2增益率4.2.3基尼指数 4.3剪枝处理4.3.1预剪枝4.3.2后剪枝 4.4连续与缺失值4.4.1连续值处理4.4.2缺失值处理 4.5多变量决策树 第四章 决策树 4.1基本流程 决策过程&#xff1a; 基本算法&#xff1a; 4.2划…

git——使用ssh连接远程仓库

文章目录 前言一. 获取邮箱和密码1. 本地配置你的名字和邮箱2. 使用命令获取你本地的邮箱和密码 二、生成ssh公钥1.任意一个文件夹路径打开Git Bash Here并输入以下命令连按三次回车2. 根据上面红框部分的地址打开文件夹3. 打开并查看id_rsa.pub 文件 三、在GitHub上连接ssh1. …

电商API知识点整理(一)商品采集接口获取商品详情数据API

商品采集接口背景 电商商品采集接口是一种机器人软件接口&#xff0c;用于从电子商务网站上爬取商品信息。它的主要作用是将电商网站上的商品信息采集和整合&#xff0c;方便用户使用。传统的商品采集需要人工收集和整理&#xff0c;工作量大、效率低&#xff1b;而电商商品采…

Flutter的文本、图片和按钮使用

像视图数据流转机制、底层渲染方案、视图更新策略等知识&#xff0c;都是构成一个UI框架的根本&#xff0c;看似枯燥&#xff0c;却往往具有最长久的生命力。 因此&#xff0c; 只有把这些最基础的知识弄明白&#xff0c;修好内功&#xff0c;才能触类旁通&#xff0c;由点及面…