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

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()
}

运行结果:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言指针(初阶)

文章目录 1:内存与地址1.1内存1.2:如何理解编址 2:指针变量与地址2.1:指针变量与解引用操作符2.1.1:指针变量2.1.2:如何拆解指针类型2.1.3:解引用操作符 2.2:指针变量的大小 3:指针变量类型的意义代码1解引用修改前解引用修改后 代码2解引用修改前解引用修改后 4:const修饰指针…

RSIC-V“一芯”学习笔记(三)——读后感以及部分PA0工作

文章目录 一、别像弱智一样提问二、提问的智慧三、安装linux以及配置问题3.1 关于问题配置 一、别像弱智一样提问 提问前&#xff0c;应该清晰问自己几个问题&#xff0c;1. 是否尝试了在搜索引擎进行搜索过2. 相关的手册和文档是否看了3. 找找有没有常见的问题文档&#xff0…

Android Jetpack:提高开发效率的终极工具集

Android Jetpack&#xff1a;提高开发效率的终极工具集 1. 引言 Android Jetpack是一套为Android应用程序开发提供帮助的工具集。它旨在简化开发流程&#xff0c;提高开发效率&#xff0c;并提供一致的用户体验。无论您是新手还是经验丰富的开发者&#xff0c;Jetpack都可以为…

命令行参数和环境变量

命令行参数 命令行参数是在用户在命令行中输入命令时&#xff0c;跟随命令一起输入的一些附加信息。这些参数可以用来配置命令的行为或传递一些数据给命令。 让同样的程序在不同的命令行参数下运行出不同的结果&#xff01; 将这些命令和参数可以传给 main 函数生&#xff0…

Junit5基础教程

文章目录 一&#xff0c;导入依赖二&#xff0c;基本功能一、常用断言二、执行顺序和常用注解1、通过BeforeAll类的注解来保证顺序2、通过order注解来保证执行顺序 三、依赖测试四、参数化测试五、测试套件SelectPackages、IncludePackages、SelectClasses、IncludeTags等注解的…

苹果手机充电充不进去怎么办?这里有你想要的一些故障排除技巧

当你的iPhone插上充电器或将其放在无线充电器上充电时,稍后再检查发现它没有充电,怎么办呢?可能的原因不少。让我们来看看一些最常见的iPhone充电问题,以及你能做些什么。 常规故障排除提示 故障排除中最基本的技术之一是用已知的好的相同组件代替不起作用的组件,如把你的…

【复现】cellinx摄像设备 未授权漏洞_50

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 cellinx是一家韩国的摄像设备 二 .漏洞影响 通过未授权访问可以创建用户进入后台&#xff0c;可能造成系统功能破坏。 三.漏洞复…

python工具方法 45 基于ffmpeg以面向对象多线程的方式实现实时推流

1、视频推流 参考基于ffmpeg模拟监控摄像头输出rtsp视频流并opencv播放 实现视频流的推流。 其基本操作就是,安装视频流推流服务器,ffmpeg,准备好要推流的视频。 命令如下所示:ffmpeg -re -stream_loop -1 -i 风景视频素材分享.flv -c copy -f rtsp rtsp://127.0.0.1:554/…

挑战杯 python的搜索引擎系统设计与实现

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python的搜索引擎系统设计与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;5分创新点&#xff1a;3分 该项目较为新颖&#xff…

租赁香港服务器多少钱一个月?24元

阿里云香港服务器2核1G、30M带宽、40GB ESSD系统盘优惠价格24元/月&#xff0c;288元一年&#xff0c;每月流量1024GB&#xff0c;多配置可选&#xff0c;官方优惠活动入口 https://t.aliyun.com/U/bLynLC 阿里云服务器网aliyunfuwuqi.com分享阿里云香港服务器优惠活动、详细配…

图像识别基础之模板匹配

principle 图像匹配 本质&#xff1a;图像的相似度很高(矩阵的相似度很高) code /*\brief 我的图像匹配函数&#xff0c;获取差方和均值最小的矩阵作为结果\param srcPicFile:用以匹配的图像文件\param templatePicFile:模板图像文件\param destPicFile:输出的检测结果文件…

【方法】如何打开带密码的RAR分卷压缩文件?

RAR分卷文件是一种特殊的RAR压缩文件格式&#xff0c;也就是将文件压缩成多个相同大小的压缩包&#xff0c;可以更方便传输。那如果收到了带有密码的RAR分卷压缩文件&#xff0c;要如何打开呢&#xff1f; 无论RAR分卷压缩文件是否设置了密码保护&#xff0c;在打开或者解压分…

数据结构与算法:双向链表

朋友们大家好啊&#xff0c;在上节完成单链表的讲解后&#xff0c;我们本篇文章来对带头循环双向链表进行讲解 双向链表 双向链表、头节点和循环的介绍构建双向链表节点的构建初始化双向循环链表&#xff08;空链表&#xff09;销毁双向链表 链表的打印双向链表头尾的插与删尾插…

基于Java (spring-boot)的房屋租赁管理系统

一、项目介绍 基于Java (spring-boot)的房屋租赁管理系统功能&#xff1a;登录、管理员、租客、公告信息管理、房屋信息管理、用户信息管理、租金信息管理、故障信息管理、房屋出租信息详情、个人信息、修改密码、等等等。 适用人群&#xff1a;适合小白、大学生、毕业设计、课…

【C语言】指针的进阶篇,深入理解指针和数组,函数之间的关系

欢迎来CILMY23的博客喔&#xff0c;本期系列为【C语言】指针的进阶篇&#xff0c;深入理解指针和数组&#xff0c;函数之间的关系&#xff0c;图文讲解其他指针类型以及指针和数组&#xff0c;函数之间的关系&#xff0c;带大家更深刻理解指针&#xff0c;以及数组指针&#xf…

谁拿了最多奖学金——NOIP 2005 提高组

输入样例&#xff1a; 4 YaoLin 87 82 Y N 0 ChenRuiyi 88 78 N Y 1 LiXin 92 88 N N 0 ZhangQin 83 87 Y N 1 输出样例&#xff1a; ChenRuiyi 9000 28700 这道题用结构体做对吧 #include <bits/stdc.h> using namespace std; class student{public:string name;int FG…

Springboot+vue的大学生智能消费记账系统的设计与实现(有报告)。Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的大学生智能消费记账系统的设计与实现&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的大学生智能消费记账系统的设计与实现&#xff0c;采…

Midjourney绘图欣赏系列(三)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子&#xff0c;它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同&#xff0c;Midjourney 是自筹资金且闭源的&#xff0c;因此确切了解其幕后内容尚不…

platformio 提示 fatal error: TimeLib.h: No such file or directory 的解决方案

在platformio编译arduino项目的时候&#xff0c;如果提示fatal error: TimeLib.h: No such file or directory&#xff0c;解决方法有2&#xff1a; 方法1&#xff1a; 在项目的platformio.ini文件中&#xff0c;添加 lib_deps # Using library Id44方法2&#xff1a; 通过…

C++ 模板进阶

C 模板进阶 一.非类型模板参数1.概念2.实例3.注意事项 二.模板的特化1.引出2.函数模板的特化1.语法和使用2.建议 3.类模板的特化1.全特化2.偏特化1.部分特化2.对参数进行进一步的限制 4.匹配顺序 三.模板的分离编译1.什么是分离编译2.模板的分离编译3.解决方法1.显式实例化(不推…