Rust 数据结构与算法:3栈:用栈实现符号匹配

news2024/11/28 19:38:35
1、符号匹配

如: (5+6)×(7+8)/(4+3)、{ { ( [ ] [ ])}}、(a+b)(c*d)func() 等各类语句的符号匹配。

这里我们关注的不是数字而是括号,因为括号更改了操作优先级,限定了语言的语义,这是非常重要的。如果括号不完整,那么整个表达式就是错的。

括号都必须以成对匹配的形式出现。括号匹配意味着每个开始符号都有相应的结束符号,并且括号必须正确嵌套,这样计算机才能正确处理。

真正具有挑战的是如何编写一个算法来从左到右读取一串符号,并决定括号是否匹配。处理的第一个左开始括号必须等待,直到其匹配最后一个右关闭括号为止。结束符号以相反的顺序匹配开始符号,从内到外,这是一个可以用栈来解决的问题。

在这里插入图片描述

括号匹配

具体算法原理:

一旦采用栈来保存括号,算法的具体实现就很简单了,因为栈的操作无非就是出入栈和判断而已。

从空栈开始,从左到右处理括号字符串。如果一个符号是开始符号,将其入栈;

如果是结束符号,则弹出栈顶元素并开始匹配这两个符号。

如果它们恰好是左右匹配的,就继续处理下一个括号,直到字符串处理完为止。

最后,当所有符号都被处理后,栈应该是空的。只要栈不为空,就说明有括号不匹配。

检测包含任意字符的字符串是否匹配的代码:

#[derive(Debug)]
struct Stack<T> {
    size: usize,  // 栈大小
    data: Vec<T>, // 栈数据
}

impl<T> Stack<T> {
    // 初始化空栈
    fn new() -> Self {
        Self {
            size: 0,
            data: Vec::new(), // 以 Vec 为低层
        }
    }

    fn is_empty(&self) -> bool {
        0 == self.size
    }

    fn len(&self) -> usize {
        self.size
    }

    // 清空栈
    fn clear(&mut self) {
        self.size = 0;
        self.data.clear();
    }

    // 将数据保存在 Vec 的末尾
    fn push(&mut self, val: T) {
        self.data.push(val);
        self.size += 1;
    }

    // 将栈顶减 1 后,弹出数据
    fn pop(&mut self) -> Option<T> {
        if 0 == self.size {
            return None;
        };
        self.size -= 1;
        self.data.pop()
    }

    // 返回栈顶数据引用和可变引用
    fn peek(&self) -> Option<&T> {
        if 0 == self.size {
            return None;
        }
        self.data.get(self.size - 1)
    }

    fn peek_mut(&mut self) -> Option<&mut T> {
        if 0 == self.size {
            return None;
        }
        self.data.get_mut(self.size - 1)
    }

    // 以下是为栈实现的迭代功能
    // into_iter: 栈改变,成为迭代器
    // iter: 栈不变,得到不可变迭代器
    // iter_mut:栈不变,得到可变迭代器

    fn into_iter(self) -> IntoIter<T> {
        // into_iter()方法获取了一个迭代器,然后进行迭代
        IntoIter(self)
    }

    fn iter(&self) -> Iter<T> {
        let mut iterator = Iter { stack: Vec::new() };
        for item in self.data.iter() {
            iterator.stack.push(item);
        }
        iterator
    }

    fn iter_mut(&mut self) -> IterMut<T> {
        let mut iterator = IterMut { stack: Vec::new() };
        for item in self.data.iter_mut() {
            iterator.stack.push(item);
        }
        iterator
    }
}
// 实现三种迭代功能
struct IntoIter<T>(Stack<T>);
// Iterator 是 Rust 的迭代器 迭代器(iterator)负责遍历序列中的每一项和决定序列何时结束的逻辑。当使用迭代器时,我们无需重新实现这些逻辑。
impl<T: Clone> Iterator for IntoIter<T> {
    // into_iter()方法获取了一个迭代器,然后进行迭代。
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        // 迭代器之所以成为迭代器,是因为实现了Iterator trait。要实现该特征,最主要的就是实现其中的 next 方法,该方法控制如何从集合中取值,最终返回值的类型是关联类型 Item。
        if !self.0.is_empty() {
            self.0.size -= 1;
            self.0.data.pop()
        } else {
            None
        }
    }
}
// 'a 生命周期标识 用于帮助编译器检查引用的有效性,避免悬垂引用和使用已被释放的内存。
// 从所有权的角度来理解,就是它可以避免因为Copy或者clone的造成的不必要开销
struct Iter<'a, T: 'a> {
    stack: Vec<&'a T>, // 'a 被用在了传参类型 T 上
}
impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T; // 生命周期标识只作用于引用上,且放在&符号之后 如这里的 &'a T
    fn next(&mut self) -> Option<Self::Item> {
        self.stack.pop()
    }
}

struct IterMut<'a, T: 'a> {
    stack: Vec<&'a mut T>,
}
impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;
    fn next(&mut self) -> Option<Self::Item> {
        self.stack.pop()
    }
}

fn main() {
    let sa = "(2+3){func}[abc]";
    let sb = "(2+3)*(3-1";
    let sc = "{{([])}}";
    let sd = "((())";
    let se = "[[[]]]]]]]]]";

    let res1 = par_checker3(sa);
    let res2 = par_checker3(sb);
    let res3 = par_checker3(sc);
    let res4 = par_checker3(sd);
    let res5 = par_checker3(se);

    println!("sa balanced: {res1}");
    println!("sb balanced: {res2}");
    println!("sc balanced: {res3}");
    println!("sd balanced: {res4}");
    println!("se balanced: {res5}");
}

// 同时检测多种开始符号和结束符号是否匹配
fn par_match(open: char, close: char) -> bool {
    let opens = "([{";
    let closers = ")]}";
    opens.find(open) == closers.find(close)
}
// 基于栈的符号匹配
fn par_checker3(par: &str) -> bool {
    let mut char_list = Vec::new();
    for c in par.chars() {
        char_list.push(c);
    }

    let mut index = 0;
    let mut balance = true;
    let mut stack = Stack::new();
    while index < char_list.len() && balance {
        let c = char_list[index];
        // 将开始符号入栈
        if '(' == c || '[' == c || '{' == c {
            stack.push(c);
        }
        // 如果是结束符号,则判断是否平衡
        if ')' == c || ']' == c || '}' == c {
            if stack.is_empty() {
                balance = false;
            } else {
                let top = stack.pop().unwrap();
                if !par_match(top, c) {
                    balance = false;
                }
            }
        }
        // 非括号字符直接跳过
        index += 1;
    }
    balance && stack.is_empty()
}

运行结果:

在这里插入图片描述

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

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

相关文章

Ps:焦点堆栈

焦点堆栈 Focus Stacking是一种摄影和图像处理技术&#xff0c;通过合并多张在不同焦距拍摄的照片来创建一张具有更大景深的图像&#xff0c;特别适用于微距摄影、风景摄影和任何需要在整个场景中保持尖锐对焦的情况。 ◆ ◆ ◆ 拍摄注意事项 1、使用三脚架 为了确保图像之间…

STM32——OLED菜单(二级菜单)

文章目录 一.补充二. 二级菜单代码 简介&#xff1a;首先在我的51 I2C里面有OLED详细讲解&#xff0c;本期代码从51OLED基础上移植过来的&#xff0c;可以先看完那篇文章&#xff0c;在看这个&#xff0c;然后按键我是用的定时器扫描不会堵塞程序,可以翻开我的文章有单独的定时…

医院三基怎么搜题答案? #学习方法#学习方法#微信

在大学生的学习过程中&#xff0c;遇到难题和疑惑是常有的事情。然而&#xff0c;随着互联网的普及和技术的发展&#xff0c;搜题和学习软件成为了大学生们解决问题的利器。今天&#xff0c;我将向大家推荐几款备受大学生喜爱的搜题和学习软件&#xff0c;帮助我们更好地应对学…

VMware虚拟机网络配置

VMware虚拟机网络配置 桥接模式NAT网络 桥接模式 桥接模式其实就是借助你宿主机上的网卡进行联网和通信&#xff0c;所以相当于虚拟机和宿主机平级&#xff0c;处于同一个网段中。 配置要点&#xff1a; 注意选择正确的宿主机网卡 查看宿主机的网络信息&#xff0c;这些信息指…

VMwareWorkstation17.0虚拟机安装Windows2.03

VMwareWorkstation17.0虚拟机安装Windows2.03 第一篇 下载Windows2.03第二篇 配置Windows2.03虚拟机机器环境第三篇 启动Windows2.03系统 第一篇 下载Windows2.03 1.Windows2.0原版软盘下载地址是 暂不提供&#xff0c;后续更新 2.Windows2.03虚拟机镜像下载地址是 Windows2…

ChatGPT高效提问—prompt实践(白领助手)

ChatGPT高效提问—prompt实践&#xff08;白领助手&#xff09; ​ 随着社会的不断发展&#xff0c;白领的比例越来越高。白领的工作通常较为繁忙&#xff0c;需要管理复杂的项目。工作量大、要求高、任务紧急&#xff0c;时间分配不当部分可能导致工作效率低下&#xff0c;任…

云备份项目:在云端保护您的数据【二、开发】

☘️过度的信息对一个过着充实生活的人来说&#xff0c;是一种不必要的负担☘️ 文章目录 前言工具类实现文件实用工具类代码实现 Json实用工具类代码实现 服务端单例配置类系统配置信息单例配置类 数据管理类数据信息数据管理 热点管理类业务处理类 客户端数据管理类文件备份类…

基于微信小程序的智能社区服务小程序,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

嵌入式中UART通信的方法

UART是一种异步全双工串行通信协议&#xff0c;由 Tx 和 Rx 两根数据线组成&#xff0c;因为没有参考时钟信号&#xff0c;所以通信的双方必须约定串口波特率、数据位宽、奇偶校验位、停止位等配置参数&#xff0c;从而按照相同的速率进行通信。 异步通信以一个字符为传输单位…

HDR 摄影

HDR 摄影&#xff0c;即高动态范围 High Dynamic Range摄影&#xff0c;旨在通过合并不同曝光值的照片来捕捉场景中从最亮到最暗部分的全部细节。 这种技术对于在一个图像中展现广泛的亮度范围特别有用&#xff0c;尤其是在自然光线条件下&#xff0c;如直射日光或阴影区域&…

[BUUCTF]-PWN:ciscn_2019_final_3解析

查看保护 RELRO保护为FULL&#xff0c;got表不可修改&#xff0c;只能修改hook表了 查看ida 这里的大致功能为alloc创建堆块&#xff08;可填充内容&#xff09;、free释放堆块&#xff08;但是不清空指针&#xff09; 值得注意的就是创建堆块大小不可以超过0x78&#xff08;…

HTB pwn Evil Corp

程序分析 初始化功能 申请了一块正常块和一块可以执行shellcode的空间 登录 向正常块写入数据 字符宽度转换 漏洞分析 所有的输入都使用了fgetws函数这个函数,这个函数会把整个串的每一个字符按最低两个宽来接收输入,如果存在unicode32等等更宽的字符这个函数会把每个字符都…

积分(一)——复化梯形积分(C++)

梯形积分 设f(x)在[a,b]上连续&#xff0c;若要计算其积分&#xff0c;则 ∫ a b f ( x ) d x F ( b ) − F ( a ) \int_a^b f(x)dx F(b) - F(a) ∫ab​f(x)dxF(b)−F(a) 其中F(x)为f(x)的原函数&#xff0c;但是计算机直接计算出该积分函数比较困难&#xff0c;因此需要近…

六、Datax通过json字符串运行

Datax通过json字符串运行 一、场景二、代码实现 一、场景 制作一个web应用&#xff0c;在页面上配置一个json字符串&#xff0c;保存在数据库里面。在执行json的时候&#xff0c;动态在本地创建一个json文件后执行&#xff0c;并识别是否成功&#xff0c;将执行过程保存在数据…

fgets的使用方法详解

fgets的使用 文章目录 fgets的使用前言&#xff08;吹水&#xff0c;不看也罢&#xff09;fgets 的基本语法使用示例fgets() 对输入的处理的特点gets() 与 fgets() 的主要区别 总结 前言&#xff08;吹水&#xff0c;不看也罢&#xff09; 鼠鼠今天在B站上大学的时候&#xff…

离散傅里叶变换

目标 我们将寻求以下问题的答案&#xff1a; 什么是傅里叶变换&#xff0c;为什么要使用它&#xff1f;如何在OpenCV中做到这一点&#xff1f;使用以下函数&#xff1a;copyMakeBorder&#xff08;&#xff09; &#xff0c; merge&#xff08;&#xff09; &#xff0c; dft…

蓝桥杯:C++排列与组合

排列是暴力枚举时的常见操作。有以下两种情况。 C的 next_permutation()是全排列函数&#xff0c;只能输出序列中所有元素的全排列。 本节将给出手写排列和组合的代码。因为在很多场合中不能使用系统自带的排列函数&#xff0c;所以需要自己编写。 全排列函数&#xff1a;nex…

Lag-Llama:第一个时间序列预测的开源基础模型介绍和性能测试

2023年10月&#xff0c;我们发表了一篇关于TimeGPT的文章&#xff0c;TimeGPT是时间序列预测的第一个基础模型之一&#xff0c;具有零样本推理、异常检测和共形预测能力。 虽然TimeGPT是一个专有模型&#xff0c;只能通过API访问。但是它还是引发了对时间序列基础模型的更多研…

读十堂极简人工智能课笔记03_遗传算法与进化

1. 寻找正确答案 1.1. 卡尔西姆斯 1.1.1. 计算机图形艺术家和研究者 1.1.2. 演示过数字进化之创造性和新颖性的先驱 1.1.3. 1994年 1.1.3.1. 创造一批能游泳、走路、跳跃&#xff0c;甚至互相竞争的虚拟动物震惊了整个科学界 1.1.3.2. 它们的人工大脑却是个极其复杂的网络…

公需课考试怎么搜题找答案?推荐你使用这5个公众号和工具 #知识分享#其他#知识分享

大学生必备&#xff0c;这条笔记大数据一定定要推给刚上大学的学弟学妹&#xff01;&#xff01; 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试…