【数据结构】栈的应用

news2024/11/27 20:23:23

目录

0  引言

1  栈在括号匹配中的应用

2  栈在表达式求值中的应用

        2.1 算数表达式

        2.2 中缀表达式转后缀表达式

2.3 后缀表达式求值

3  栈在递归中的应用

3.1 栈在函数调用中的作用

3.2 栈在函数调用中的工作原理

4  总结


0  引言

        栈(Stack)是一种非常基本且重要的数据结构,它们在许多计算机科学和软件工程的应用中都有广泛的用途。

        栈:

                ①括号匹配;

                ②表达式求值;

                ③递归函数调用。

1  栈在括号匹配中的应用

        表达式中有两种括号:圆括号 ( ) 和 方括号 [ ],嵌套的顺序任意,但应为正确的格式。

        例如:( ( [ ] [ ] ) ) 为正确格式。

        但如何用算法实现括号匹配问题?

        思路如下:

        (1)初始一个空栈;

        (2)顺序读入括号;

        (3)当读入的为左括号,将继续读入括号,直到读入第一个右括号。那将检测与之最近的左括号是否与之相匹配,若匹配,则出栈;若不匹配,则退出程序。当程序结束时,栈为空。反之,则表明括号序列的格式不正确。

        代码如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <stdbool.h>  
  
#define MAX_SIZE 100 // 假设栈的最大大小  
  
typedef struct {  
    char data[MAX_SIZE];  
    int top;  
} Stack;  
  
// 初始化栈  
void initStack(Stack *s) {  
    s->top = -1;  
}  
  
// 判断栈是否为空  
bool isEmpty(Stack *s) {  
    return s->top == -1;  
}  
  
// 入栈  
void push(Stack *s, char c) {  
    if (s->top >= MAX_SIZE - 1) {  
        printf("Stack overflow\n");  
        return;  
    }  
    s->data[++s->top] = c;  
}  
  
// 出栈  
char pop(Stack *s) {  
    if (isEmpty(s)) {  
        printf("Stack underflow\n");  
        return '#'; // 返回一个无效字符,或可以选择抛出一个错误  
    }  
    return s->data[s->top--];  
}  
  
// 检查两个括号是否匹配  
bool isMatch(char c1, char c2) {  
    if (c1 == '(' && c2 == ')') return true;  
    if (c1 == '[' && c2 == ']') return true;  
    if (c1 == '{' && c2 == '}') return true;  
    return false;  
}  
  
// 括号匹配函数  
bool isBalanced(char *str) {  
    Stack s;  
    initStack(&s);  
  
    for (int i = 0; str[i] != '\0'; i++) {  
        if (str[i] == '(' || str[i] == '[' || str[i] == '{') {  
            push(&s, str[i]);  
        } else if (str[i] == ')' || str[i] == ']' || str[i] == '}') {  
            if (isEmpty(&s)) {  
                // 栈为空,但遇到了右括号,不匹配  
                return false;  
            }  
            char topChar = pop(&s);  
            if (!isMatch(topChar, str[i])) {  
                // 栈顶元素与当前右括号不匹配  
                return false;  
            }  
        }  
    }  
  
    // 如果栈为空,则所有括号都匹配  
    return isEmpty(&s);  
}  
  
int main() {  
    char str[MAX_SIZE];  
    printf("Enter a string with brackets: ");  
    scanf("%s", str); 

    if (isBalanced(str)) {  
        printf("The brackets are balanced.\n");  
    } else {  
        printf("The brackets are not balanced.\n");  
    }  
  
    return 0;  
}

2  栈在表达式求值中的应用

        2.1 算数表达式

        中缀表达式是人们常用的算术表达式,即操作符以中缀形式处于操作数之间。但在计算机中,中缀表达式相较于前缀和后缀表达式来说,更不易被计算机识别。前缀表达式成为波兰式,后缀表达式又称逆波兰式。

        2.2 中缀表达式转后缀表达式

        (1)手算方法:

        ①根据运算顺序对表达式运算符排号;

        ②根据运算符排号顺序,将运算符及两端的操作数以(左操作数 右操作数 运算符)的顺序重新组合。

        例如:( A + B ) * C + ( D - E ) / F 转后缀表达式的过程如下:

        (2)算法实现:

        ①初始一个栈;

        ②遇到操作数,直接加入后缀表达式;

        ③遇到界限符,若为左括号直接入栈,若为右括号,则依次弹出栈中的运算符,加入后缀表达式,知道弹出左括号为止。需要注意的是,左括号和右括号直接删除,不加入后缀表达式。

        ④遇到运算符,则看运算符的优先级,若高于除左括号外的栈顶元素,则直接入栈。反之,则依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,直到遇到低于他的优先级的运算符,才入栈。

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <stdbool.h>  
  
#define MAX_SIZE 100  
  
typedef struct {  
    char data[MAX_SIZE];  
    int top;  
} Stack;  
  
// 初始化栈  
void initStack(Stack *s) {  
    s->top = -1;  
}  
  
// 判断栈是否为空  
bool isEmpty(Stack *s) {  
    return s->top == -1;  
}  
  
// 入栈  
bool push(Stack *s, char c) {  
    if (s->top >= MAX_SIZE - 1) {  
        return false; // 栈溢出  
    }  
    s->data[++s->top] = c;  
    return true;  
}  
  
// 出栈  
char pop(Stack *s) {  
    if (isEmpty(s)) {  
        return '\0'; // 栈空,返回空字符  
    }  
    return s->data[s->top--];  
}  
  
// 获取栈顶元素,但不弹出  
char peek(Stack *s) {  
    if (isEmpty(s)) {  
        return '\0'; // 栈空,返回空字符  
    }  
    return s->data[s->top];  
}  
  
// 运算符的优先级比较(这里只处理了基本的四则运算)  
int precedence(char op) {  
    if (op == '+' || op == '-') {  
        return 1;  
    }  
    if (op == '*' || op == '/') {  
        return 2;  
    }  
    return 0; // 如果不是运算符,返回0  
}  
  
// 将中缀表达式转换为后缀表达式  
void infixToPostfix(char *infix, char *postfix) {  
    Stack s;  
    initStack(&s);  
    int i = 0, j = 0;  
  
    while (infix[i] != '\0') {  
        if (infix[i] >= '0' && infix[i] <= '9') {  
            // 如果是操作数,直接添加到后缀表达式中  
            postfix[j++] = infix[i++];  
            postfix[j++] = ' '; // 假设操作数都是个位数,用空格分隔  
        } else if (infix[i] == '(') {  
            // 如果是左括号,直接入栈  
            push(&s, infix[i++]);  
        } else if (infix[i] == ')') {  
            // 如果是右括号,则弹出栈中元素直到遇到左括号  
            while (!isEmpty(&s) && peek(&s) != '(') {  
                postfix[j++] = pop(&s);  
                postfix[j++] = ' ';  
            }  
            // 弹出左括号,但不加入后缀表达式  
            pop(&s);  
            i++;  
        } else {  
            // 如果是运算符  
            while (!isEmpty(&s) && precedence(peek(&s)) >= precedence(infix[i])) {  
                // 如果栈不为空且栈顶元素优先级高于或等于当前运算符,弹出栈顶元素  
                postfix[j++] = pop(&s);  
                postfix[j++] = ' ';  
            }  
            // 当前运算符入栈  
            push(&s, infix[i++]);  
        }  
    }  
  
    // 弹出栈中剩余的所有运算符  
    while (!isEmpty(&s)) {  
        postfix[j++] = pop(&s);  
        postfix[j++] = ' ';  
    }  
  
    // 添加字符串结束符  
    postfix[j] = '\0';  
}  
  
int main() {  
    char infix[MAX_SIZE], postfix[MAX_SIZE * 2]; // 后缀表达式可能更长,因此分配更多空间  
  
    printf("Enter an infix expression: ");  
    scanf("%s", infix); // 注意:这里不会处理空格和复杂输入  
  
    infixToPostfix(infix, postfix);  
  
    printf("Postfix expression: %s\n", postfix);  
  
    return 0;  
}

2.3 后缀表达式求值

        后缀表达式(也称为逆波兰表示法或逆波兰记法)是一种不需要括号来标明运算符的优先级的数学表达式。在这种表示法中,所有的运算符都放在操作数的后面。

        求值后缀表达式的基本步骤如下:

  • 初始化一个栈,用于存储操作数。
  • 从左到右扫描后缀表达式。
  • 如果扫描到操作数,则将其压入栈中。
  • 如果扫描到运算符,则从栈中弹出两个操作数(先弹出的为右操作数,后弹出的为左操作数),将这两个操作数作为运算符的输入进行运算,然后将结果压回栈中。
  • 重复步骤2-4,直到后缀表达式扫描完毕。
  • 栈中剩下的元素就是表达式的值。

        示例

        后缀表达式:3 4 + 5 *

        求值过程:

  1. 扫描到 3,压入栈:[3]
  2. 扫描到 4,压入栈:[3, 4]
  3. 扫描到 +,弹出 4 和 3,计算 3 + 4 得到 7,压入栈:[7]
  4. 扫描到 5,压入栈:[7, 5]
  5. 扫描到 *,弹出 5 和 7,计算 7 * 5 得到 35,压入栈:[35]
  6. 扫描完毕,栈中元素 35 即为表达式的值。

        下面是实现代码(以上述示例为例):

#include <stdio.h>  
#include <stdlib.h>  
#include <ctype.h>  
#include <string.h>  
  
#define MAX_STACK_SIZE 100  
  
typedef struct {  
    double data[MAX_STACK_SIZE];  
    int top;  
} Stack;  
  
// 初始化栈  
void initStack(Stack *s) {  
    s->top = -1;  
}  
  
// 判断栈是否为空  
int isEmpty(Stack *s) {  
    return s->top == -1;  
}  
  
// 压栈操作  
void push(Stack *s, double value) {  
    if (s->top >= MAX_STACK_SIZE - 1) {  
        printf("Stack overflow\n");  
        exit(1);  
    }  
    s->data[++s->top] = value;  
}  
  
// 弹栈操作  
double pop(Stack *s) {  
    if (isEmpty(s)) {  
        printf("Stack underflow\n");  
        exit(1);  
    }  
    return s->data[s->top--];  
}  
  
// 求值后缀表达式  
double evaluatePostfix(const char *postfix) {  
    Stack s;  
    initStack(&s);  
  
    const char *token = strtok((char *)postfix, " "); // 假设操作符和操作数之间用空格分隔  
    while (token != NULL) {  
        if (isdigit(token[0])) { // 如果是操作数  
            double value = atof(token);  
            push(&s, value);  
        } else { // 如果是运算符  
            double rightOperand = pop(&s); // 弹出右操作数  
            double leftOperand = pop(&s); // 弹出左操作数  
  
            switch (token[0]) {  
                case '+':  
                    push(&s, leftOperand + rightOperand);  
                    break;  
                case '-':  
                    push(&s, leftOperand - rightOperand);  
                    break;  
                case '*':  
                    push(&s, leftOperand * rightOperand);  
                    break;  
                case '/':  
                    if (rightOperand != 0.0) {  
                        push(&s, leftOperand / rightOperand);  
                    } else {  
                        printf("Error: Division by zero\n");  
                        exit(1);  
                    }  
                    break;  
                default:  
                    printf("Error: Unknown operator\n");  
                    exit(1);  
            }  
        }  
        token = strtok(NULL, " "); // 继续获取下一个token  
    }  
  
    if (!isEmpty(&s)) {  
        return pop(&s); // 栈中剩下的元素就是表达式的值  
    } else {  
        printf("Error: Invalid postfix expression\n");  
        exit(1);  
    }  
}  
  
int main() {  
    const char *postfix = "3 4 + 5 *";  
    double result = evaluatePostfix(postfix);  
    printf("Result: %lf\n", result);  
    return 0;  
}

3  栈在递归中的应用

3.1 栈在函数调用中的作用

  • 参数传递:当调用一个函数时,需要传递参数给该函数。这些参数会被压入栈中,以便函数内部能够访问和使用它们。
  • 局部变量分配:函数内部定义的局部变量会在栈上分配空间。这些变量的生命周期与函数的执行周期相同,当函数执行完毕后,这些局部变量所占用的栈空间会被自动释放。
  • 保存调用的返回地址:在函数调用时,CPU需要知道函数执行完毕后应该返回到哪个位置继续执行。这个返回地址会被保存在栈中,以便函数执行完毕后能够正确地返回到调用它的位置。
  • 保存寄存器以供恢复:在函数调用和返回的过程中,CPU的寄存器状态会发生变化。为了能够在函数返回后恢复原来的寄存器状态,栈会保存这些寄存器的值。

3.2 栈在函数调用中的工作原理

  • 函数调用:当调用一个函数时,系统首先会创建一个新的栈帧(stack frame)来保存该函数的执行环境。这个栈帧包含了函数的返回地址、参数、局部变量等信息。然后,系统会将当前程序的执行状态(如返回地址、寄存器状态等)压入栈中,以便在函数执行完毕后能够恢复。
  • 函数执行:在函数执行过程中,函数会访问栈帧中的参数和局部变量,并根据需要进行计算和操作。同时,如果函数内部调用了其他函数,系统也会为这些被调用的函数创建新的栈帧,并将当前函数的执行状态压入栈中保存。
  • 函数返回:当函数执行完毕或者遇到return语句时,系统会弹出当前函数的栈帧,并根据栈帧中的返回地址返回到调用它的位置继续执行。在返回之前,系统还会恢复调用该函数时的寄存器状态。

        下面将给出一个例子:

        例如:阶乘,大家可以自行调试;

#include <stdio.h>

int step(int n){
    if(n==1)
        return 1;
    else
        return n*step(n-1);
}

int main(){
    int n,s;
    scanf("%d",&n);
    s=step(n);
    printf("%d",s);
}

4  总结

        在本文中,我们深入探讨了栈这一数据结构及其在各种应用场景中的重要作用。栈作为一种后进先出(LIFO)的数据结构,其独特的操作方式——压栈(push)和弹栈(pop),使得它在计算机科学和软件开发中占据了不可或缺的地位。

        详细讨论了栈在多个领域中的应用。其中,后缀表达式的求值是一个经典的栈应用示例。在这个问题中,我们利用栈来存储操作数,并通过操作数的弹出和结果的压入,实现了表达式的正确计算。这种方法不仅简化了表达式的处理流程,而且提高了计算效率。

        此外,栈还在函数调用、递归等方面发挥着重要作用。在函数调用中,栈用于存储局部变量和返回地址,确保函数能够正确地返回并继续执行。在递归算法中,栈用于保存递归调用的中间结果,从而避免重复计算。

        综上所述,栈作为一种基本而强大的数据结构,在各个领域都有着广泛的应用。通过学习和掌握栈的使用方法和应用场景,我们能够更好地解决实际问题,提高编程效率。

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

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

相关文章

Python001

Python 是一种高级编程语言。它具有以下显著特点&#xff1a;1. 简单易学&#xff1a;语法相对简洁明了&#xff0c;对初学者很友好。2. 丰富的库&#xff1a;拥有大量强大的内置库和第三方库&#xff0c;可用于各种领域&#xff0c;如数据分析、机器学习、Web 开发等。3. 可读…

Python应用开发——30天学习Streamlit Python包进行APP的构建(5)

上几次我们已经将一些必备的内容进行了快速的梳理,让我们掌握了streanlit的凯快速上手,接下来我们将其它的一些基础函数再做简单的梳理,以顺便回顾我们未来可能用到的更丰富的函数来实现应用的制作。 st.write_stream 将生成器、迭代器或类似流的序列串流到应用程序中。 …

【网络编程开发】8.TCP连接管理与UDP协议 9.IP协议与ethernet协议

8.TCP连接管理与UDP协议 三次握手 三次握手的过程在TCP/IP网络通信中起着至关重要的作用&#xff0c;它不仅确保了数据的可靠传输&#xff0c;还为两端的数据传输提供了稳定的连接初始化过程。这一过程涉及到几个关键步骤&#xff0c;每个步骤都有其特定的目的和功能。 步骤&…

你可以直接和数据库对话了!DB-GPT 用LLM定义数据库下一代交互方式,数据库领域的GPT、开启数据3.0 时代

✨点击这里✨&#xff1a;&#x1f680;原文链接&#xff1a;&#xff08;更好排版、视频播放、社群交流、最新AI开源项目、AI工具分享都在这个公众号&#xff01;&#xff09; 你可以直接和数据库对话了&#xff01;DB-GPT 用LLM定义数据库下一代交互方式&#xff0c;数据库领…

前端开发高频面试题

好的&#xff0c;以下是对您提出的问题的详细回答&#xff1a; 说说vue动态权限绑定渲染列表&#xff08;权限列表渲染&#xff09; Vue中动态权限绑定渲染列表通常涉及以下步骤&#xff1a; 首先&#xff0c;通过API请求从服务器获取当前用户的权限数据。在Vue组件中&#xff…

Excel 生成所在月份的每一天列表

Excel 的 A2 格是日期 A1Fecha201/03/24 需要生成该日期所在月份的每一天的列表 A1WholeMonth201/03/24302/03/24403/03/24504/03/24605/03/24706/03/24807/03/24908/03/241009/03/241110/03/241211/03/241312/03/241413/03/241514/03/241615/03/241716/03/241817/03/241918…

【稳定检索/投稿优惠】2024年智慧金融与财务管理国际会议(SFFM 2024)

2024 International Conference on Smart Finance and Financial Management 2024年智慧金融与财务管理国际会议 【会议信息】 会议简称&#xff1a;SFFM 2024 截稿时间&#xff1a;以官网为准 大会地点&#xff1a;中国广州 会议官网&#xff1a;www.iacsffm.com 会议邮箱&am…

【python】OpenCV—Cartoonify and Portray

参考来自 使用PythonOpenCV将照片变成卡通照片 文章目录 1 卡通化codecv2.medianBlurcv2.adaptiveThresholdcv2.kmeanscv2.bilateralFilter 2 肖像画cv2.divide 1 卡通化 code import cv2 import numpy as npdef edge_mask(img, line_size, blur_value):gray cv2.cvtColor(…

idea2023如何创建普通maven工程项目

解决 1.创建新项目 1.进入创建项目 File -> new -> project 2&#xff0c;project 中有 build system 选择maven 2.在已有项目中创建普通maven工程 1.右键项目选择 new -> Module 2.选择 new Module 其实与新建maven工程没什么区别 em:问题 idea以前的版本是在Mav…

【一百一十】【算法分析与设计】[SDOI2009] HH的项链,树状数组应用,查询区间的种类数,树状数组查询区间种类数

P1972 [SDOI2009] HH的项链 [SDOI2009] HH的项链 题目描述 HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运&#xff0c;所以每次散步完后&#xff0c;他都会随意取出一段贝壳&#xff0c;思考它们所表达的含义。HH 不断地收集新的贝壳&#xff0c;因此&am…

第十二届蓝桥杯C++青少年组中/高级组选拔赛2020年11月22日真题解析

一、编程题 第1题&#xff1a;求和 【题目描述】 输入一个正整数 N(N < 100)&#xff0c;输出 1 到 N(包含 1 和 N)之间所有奇数的和。 【输入描述】 输入一个正整数 N(N < 100) 【输出描述】 输出 1 到 N 之间的所有奇数的和 【输入样例】 3【输出样例】 4答案&…

Llama模型家族之拒绝抽样(Rejection Sampling)(九) 强化学习之Rejection Sampling

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;一&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 基于 LlaMA…

利用streamlit结合langchain_aws实现claud3的页面交互

测试使用的代码如下 import streamlit as st from langchain_aws import ChatBedrockdef chat_with_model(prompt, model_id):llm ChatBedrock(credentials_profile_name"default", model_idmodel_id, region_name"us-east-1")res llm.invoke(prompt)re…

UiPath发送邮件给多人时需要注意哪些限制?

UiPath发送邮件给多人的步骤&#xff1f;如何使用UiPath发信&#xff1f; 尽管UiPath提供了强大的邮件发送功能&#xff0c;但在批量发送邮件时&#xff0c;有一些限制和注意事项是我们必须了解的。AokSend将详细介绍这些限制&#xff0c;并提供一些优化建议。 UiPath发送邮件…

视频监控管理平台LntonCVS视频汇聚平台充电桩视频监控应用方案

随着新能源汽车的广泛使用&#xff0c;公众对充电设施的安全性和可靠性日益重视。为了提高充电桩的安全管理和站点运营效率&#xff0c;LntonCVS公司推出了一套全面的新能源汽车充电桩视频监控与管理解决方案。 该方案通过安装高分辨率摄像头&#xff0c;对充电桩及其周边区域进…

纷享销客安全体系:安全合规认证

安全合规认证是指组织通过独立的第三方机构对其信息系统和数据进行评估和审查&#xff0c;以确认其符合相关的安全标准、法律法规和行业要求的过程。 安全合规认证可以帮助组织提高信息系统和数据的安全性&#xff0c;并向客户、合作伙伴和监管机构证明其符合相关的安全标准和…

python协程入门实战详解

本章将以通俗易懂、贴合实际的方式介绍以下内容&#xff1a; 协程是什么&#xff0c;有什么特点&#xff0c;协程的优势是什么如何理解事件和事件循环协程的创建方式&#xff0c;如何控制协程的并发量在协程中使用aiohttp发送HTTP请求aiohttp案例协程中的异常处理&#xff0c;…

如何使用Python的Turtle模块绘制小猪

一、前置条件 在开始学习如何使用Python的Turtle模块进行绘画之前&#xff0c;请确保你的电脑已安装Python环境。如果尚未安装Python&#xff0c;你可以从Python官网下载并安装最新版本。 Turtle模块是Python内置的一个用于绘图的库&#xff0c;通常不需要额外安装。如果你发…

使用 Ollama 和 Open WebUI 自托管 LLM 聊天机器人(无需 GPU)

✨点击这里✨&#xff1a;&#x1f680;原文链接&#xff1a;&#xff08;更好排版、视频播放、社群交流、最新AI开源项目、AI工具分享都在这个公众号&#xff01;&#xff09; 使用 Ollama 和 Open WebUI 自托管 LLM 聊天机器人&#xff08;无需 GPU&#xff09; &#x1f31…

linux指令--sed

sed 主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。 语法解析 sed [选项] 编辑命令 文件 选项&#xff1a; -n&#xff1a;只显示匹配处理的行-e&#xff1a;执行多个编辑命令时-i&#xff1a;在原文件中进行修改&#xff0c;不输出到屏幕-…