数据结构:栈的应用举例——括号匹配的检验

news2025/3/21 2:58:05

2. 括号匹配的检验

如果表达式中包含括号,当程序中含有这类表达式时,在代码编译过程中,必然会检查括号是否匹配,这是一项必需的语法检查环节。

(1)迭代版

此处假设表达式中只含有左、右圆括号,借助栈检验左右圆括号是否匹配。

  • 每当读入一个左括号,则直接入栈,等待相匹配的右括号;
  • 每当读入一个右括号,若栈顶为左括号,将当前栈顶的左括号出栈。否则返回 false (表示不匹配);
  • 直到表达式扫描完毕,且栈为空时返回 true,否则返回 false

【算法描述】

//括号匹配检验:迭代版
bool math(char exp[], int n){
    int i = 0;
    char e;
    bool match = true;
    LinkStack * st;  //本例使用链栈,使用顺序栈亦可
    InitStack(st);   //初始化栈
    while(i < n && match){//扫描 exp 中的所有字符
        if(exp[i] == "("){//当前字符为左括号,将其入栈
            Push(st, exp[i]);
        } else if (exp[i] == ")"){//当前字符为右括号
            if(GetTop(st, e) == true){//成功取到栈顶元素 e
                if (e != '(')    //栈顶元素不为 '('
                    match = false; //表示不匹配
                else             //栈顶元素为 '('
                    Pop(st, e)//将栈顶元素出栈
            } else {
                match = false;  //无法取栈顶元素时表示不匹配
            }
        }
        i++;  //继续处理其他字符
    }
    if(!StackEmpty(st))  //栈不空时表示不匹配
        match = false;
    DestroyStack(st);  //销毁栈
    return match;
}

【算法分析】

  • 时间复杂度: O ( n ) O(n) O(n) n n n 是字符串的长度
  • 算法运行过程中的辅助空间是栈 st ,其大小不会超过 n n n ,故空间复杂度为 O ( n ) O(n) O(n)

此算法的流程控制简单,而且便于推广至多类括号并存的场合。它自左向右逐个考查各字符, 忽略所有非括号字符。凡遇到左括号,无论属于哪类均统一压入栈中。若遇右括号,则弹出栈顶的左括号并与之比对。若二者属于同类,则继续检查下一字符;否则,即可断定表达式不匹配。 当然,栈提前变空或者表达式扫描结束后栈非空,也意味着不匹配。

(2)递归版

仅仅考虑圆括号,一般地,可以将表达式 S 分解为下面的形式:
S = S 0 + " ( " + S 1 + " ) " + S 2 + S 3 S = S_0+^"(^"+S_1+^")^"+S_2+S_3 S=S0+"("+S1+")"+S2+S3
其中 S 0 S_0 S0 S 3 S_3 S3 不含括号。

很显然,当且仅当 S 1 S_1 S1 S 2 S_2 S2 的括号匹配(上式中为了表示明显地表示括号,将 " ( " ^"(^" "(" S 1 S_1 S1 " ) " ^")^" ")" S 2 S_2 S2 分开,事实上左右括号应该分别包含在 S 1 S_1 S1 S 2 S_2 S2 之内),表达式 S S S 的括号必匹配。

因此,可以使用分治算法,将表达式 S S S 划分为子表达式 S 0 、 S 1 、 S 2 、 S 3 S_0、S_1、S_2、S_3 S0S1S2S3 ,分别递归地判断 S 1 S_1 S1 S 2 S_2 S2 是否匹配。

【算法描述】

void trim(char exp[], int &lo, int &hi){//删除 exp[lo, hi]不含括号的最长前缀、后缀
    while((lo <= hi) && (exp[lo] != '(') && (exp[lo] != ')'))
        lo++; //查找第一个括号
    while((lo <= hi) && (exp[hi] != '(') && (exp[hi] != ')'))
        hi--; //查找最后一个括号
}

int divide(char exp[], int lo, int hi){//切分 exp[lo, hi],使 exp 匹配仅当子表达式匹配
    int mi = lo;
    int crc = 1; //crc 为 [lo, mi] 范围内左右括号的数目之差
    while((0 < crc) && (++mi < hi)){//逐个检查字符,直到左右括号数目相等或者越界
        if(exp[mi] == ')')
            crc--; //遇到右括号减一
        if(exp[mi] == '(')
            crc++; //遇到左括号加一
    }
    return mi; //若 mi<=hi,则为合法切分点;否则,意味着局部不匹配
}

bool math(char exp[], int lo, int hi){
    trim(exp, lo, hi); //清除不含括号的前缀、后缀
    if (lo > hi)
        return true;
    if(exp[lo] != '(')
        return false; //首字符非左括号,则必不匹配
    if(exp[hi] != ')')
        return false; //末字符非右括号,则必不匹配
    int mi = divide(exp, lo, hi); //确定适当的切分点
    if(mi > hi)
        return false; //切分点不合法,意味着局部以至整体不匹配
    return math(exp, lo+1, mi-1) && math(exp, mi+1, hi); //分别检查左、右子表达式
}
  • trim() 函数用于截除表达式中不含括号的头部和尾部,即前缀 S 0 S_0 S0 和后缀 S 3 S_3 S3
  • divide() 函数对表达式做线性扫描,并动态地记录已经扫描的左、右括号数目之差。如此,当已扫过同样多的左、右括号时,即确定了一个合适的切分点 mi,并得到子表达式 S_1 = exp(lo, mi)S_2 = exp(mi, hi]
  • 递归地检查 S 1 S_1 S1 S 2 S_2 S2 ,即可判断原表达式是否匹配。

【算法分析】

在最坏情况下,divide() 需要线性时间,且递归深度为 O ( n ) O(n) O(n) ,故以上算法共需 O ( n 2 ) O(n^2) O(n2) 时间。

注意:上述方法难以处理含有多种括号的表达式,故不如迭代版适用性广。

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

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

相关文章

深圳南柯电子|医疗设备EMC检测测试整改:保障患者安全的第一步

在医疗设备领域&#xff0c;电磁兼容性&#xff08;EMC&#xff09;是确保设备安全、有效运行的关键指标。随着医疗技术的飞速发展&#xff0c;医疗设备日益复杂&#xff0c;其电磁环境也愈发复杂多变。EMC检测测试及整改因此成为医疗设备研发、生产、销售过程中不可或缺的一环…

【笔记】计算机网络——数据链路层

概述 链路是从一个结点到相邻结点的物理路线&#xff0c;数据链路则是在链路的基础上增加了一些必要的硬件和软件实现 数据链路层位于物理层和网络层之间&#xff0c;它的核心任务是在直接相连的节点&#xff08;如相邻的交换机&#xff0c;路由器&#xff09;之间提供可靠且…

STM32-汇编

学习arm汇编的主要目的是为了编写arm启动代码&#xff0c;启动代码启动以后&#xff0c;引导程序到c语言环境下运行。换句话说启动代码的目的是为了在处理器复位以后搭建c语言最基本的需求。因此启动代码的主要任务有&#xff1a; 初始化异常向量表&#xff1b; 初始化各工作模…

利用通义灵码AI在VS Code中快速开发扫雷游戏:Qwen2.5-Max模型的应用实例

引言 随着人工智能技术的不断进步&#xff0c;开发过程中的自动化程度也在逐步提高。阿里云推出的通义灵码AI程序员&#xff0c;作为一款创新型的智能编程助手&#xff0c;现已全面上线并兼容VS Code、JetBrains IDEs等多种开发环境。本文将介绍如何利用最新的Qwen2.5-Max模型…

202503执行jmeter压测数据库(ScyllaDB,redis,lindorm,Mysql)

一、Mysql 1 、 准备MySQL 连接内容 2 、 下载连接jar包 准备 mysql-connector-java-5.1.49.jar 放到 D:\apache-jmeter-5.6.3\lib\ext 目录下面; 3 、 启动jmeter ,配置脚本 添加线程组---》JDBC Connection Configuration---》JDBC Request---》查看结果树。 1)测…

以太网 MAC 帧格式

文章目录 以太网 MAC 帧格式以太网帧间隔参考 本文为笔者学习以太网对网上资料归纳整理所做的笔记&#xff0c;文末均附有参考链接&#xff0c;如侵权&#xff0c;请联系删除。 以太网 MAC 帧格式 以太网技术的正式标准是 IEEE 802.3&#xff0c;它规定了以太网传输数据的帧结…

【PCB工艺】基础:电子元器件

电子原理图&#xff08;Schematic Diagram&#xff09;是电路设计的基础&#xff0c;理解电子元器件和集成电路&#xff08;IC&#xff09;的作用&#xff0c;是画好原理图的关键。 本专栏将系统讲解 电子元器件分类、常见 IC、电路设计技巧&#xff0c;帮助你快速掌握电子电路…

从WebRTC到嵌入式:EasyRTC如何借助大模型提升音视频通信体验

随着人工智能技术的快速发展&#xff0c;WebRTC与大模型的结合正在为音视频通信领域带来革命性的变革。WebRTC作为一种开源实时通信技术&#xff0c;以其低延迟、跨平台兼容性和强大的音视频处理能力&#xff0c;成为智能硬件和物联网设备的重要技术支撑。 而EasyRTC作为基于W…

前端样式库推广——TailwindCss

官方网址&#xff1a; https://tailwindcss.com/docs/installation/using-vite 中文官方文档&#xff1a;https://www.tailwindcss.cn/ github地址&#xff1a;tailwindcss 正在使用tailwindcss的网站&#xff1a;https://tailwindcss.com/showcase 一看github&#xff0c;竟然…

SpringBoot 第二课(Ⅰ) 整合springmvc(详解)

目录 一、SpringBoot对静态资源的映射规则 1. WebJars 资源访问 2. 静态资源访问 3. 欢迎页配置 二、SpringBoot整合springmvc 概述 Spring MVC组件的自动配置 中央转发器&#xff08;DispatcherServlet&#xff09; 控制器&#xff08;Controller&#xff09; 视图解…

OpenHarmony 开源鸿蒙北向开发——3.配置SDK

安装、配置完成之后我们就要配置SDK。 我们创建工程后&#xff0c;点击右上角设置 进入设置 进入OpenHarmony SDK&#xff0c;选择编辑 这里配置一下SDK安装位置 点击完成 这里我们API版本勾选第一个即可 确认安装 勾选接受 这里要等一会 安装完成后&#xff0c;点击完成

vulhub Matrix-Breakout

1.下载靶机&#xff0c;打开靶机和kali虚拟机 2.查询kali和靶机ip 3.浏览器访问 访问81端口有登陆界面 4.扫描敏感目录 kali dirb 扫描 一一访问 robot.txt提示我们继续找找&#xff0c;可能是因为我们的字典太小了&#xff0c;我们换个扫描器换个字典试下,利用kali自带的最大…

Unity3D开发AI桌面精灵/宠物系列 【二】 语音唤醒 ivw 的两种方式-Windows本地或第三方讯飞等

Unity3D 交互式AI桌面宠物开发系列【二】ivw 语音唤醒 该系列主要介绍怎么制作AI桌面宠物的流程&#xff0c;我会从项目开始创建初期到最终可以和AI宠物进行交互为止&#xff0c;项目已经开发完成&#xff0c;我会仔细梳理一下流程&#xff0c;分步讲解。 这篇文章主要讲有关于…

三月九次前端面试复盘:当场景题成为通关密钥

三月初集中面了包括字节、美团、滴滴在内的9家公司&#xff0c;经历7场技术面2场Leader面后&#xff0c;发现如今的面试逻辑已发生根本转变。这里分享真实经历与题目&#xff0c;供近期求职者参考。 一、面试形态变化&#xff1a;从理论背诵到实战推演 1. 八股文边缘化&#…

C++输入输出流第一弹:标准输入输出流 详解(带测试代码)

目录 C输入输出流 流的四种状态&#xff08;重点&#xff09; 标准输入输出流 标准输入流 逗号表达式 1. 逗号表达式的基本规则 示例 2. 图片中的代码分析 关键点解析 3. 常见误区 误区 1&#xff1a;逗号表达式等同于逻辑与 && 误区 2&#xff1a;忽略输入…

鸿蒙NEXT项目实战-百得知识库05

代码仓地址&#xff0c;大家记得点个star IbestKnowTeach: 百得知识库基于鸿蒙NEXT稳定版实现的一款企业级开发项目案例。 本案例涉及到多个鸿蒙相关技术知识点&#xff1a; 1、布局 2、配置文件 3、组件的封装和使用 4、路由的使用 5、请求响应拦截器的封装 6、位置服务 7、三…

黑马node.js教程(nodejs教程)——AJAX-Day01-04.案例_地区查询——查询某个省某个城市所有地区(代码示例)

文章目录 代码示例效果 代码示例 axiosTest.html <!DOCTYPE html> <!-- 文档类型声明&#xff0c;告诉浏览器这是一个HTML5文档 --> <html lang"en"> <!-- HTML根元素&#xff0c;设置文档语言为英语 --><head> <!-- 头部区域&am…

vue 自制列表,循环滚动

需求人员表示&#xff0c;超过高度的表格内容需要滚动展示&#xff0c;所以效果图如下&#xff1a; 自定义列表样式&#xff0c;主要是通过flex布局&#xff0c;控制 类th 与 类td 的宽度保持一致&#xff0c;标签结构还是参考了table的结构&#xff0c;由thead与tbody包裹tr再…

Windows主机、虚拟机Ubuntu、开发板,三者之间文件互传

以下内容源于日常学习的整理&#xff0c;欢迎交流。 下图是Windows主机、虚拟机Ubuntu、开发者三者之间文件互传的方式示意图&#xff1a; 注意&#xff0c;下面谈及的所有方式&#xff0c;都要求两者的IP地址处于同一网段&#xff0c;涉及到的软件资源见felm。 一、Windows主…

Windows Docker 报错: has no HTTPS proxy,换源

pull python 3.7报错&#xff1a; 尝试拉取Docker 测试库hello world也失败 尝试使用临时镜像源&#xff0c;可以成功拉取&#xff1a; sudo docker pull docker.m.daocloud.io/hello-world说明确实是网络问题&#xff0c;需要配置镜像源&#xff0c;为了方便&#xff0c;在d…