Rust 数据结构与算法:5栈:用栈实现前缀、中缀、后缀表达式

3、前缀、中缀和后缀表达式

计算机是从左到右处理数据的,类似(A + (B * C))这样的完全括号表达式,计算机如何跳到内部括号计算乘法,然后跳到外部括号计算加法呢?

一种直观的方法是将运算符移到操作数外,分离运算符和操作数。计算时先取运算符再取操作数,计算结果则作为当前值参与后面的运算,直到完成对整个表达式的计算。

可将中缀表达式A + B中的“+”移出来,既可以放前面,也可以放后面,得到的将是+ A B和A B +。这两种不同的表达式分别被称为前缀表达式和后缀表达式。

前缀表达式要求所有运算符在处理的两个操作数之前,后缀表达式则要求所有运算符在相应的操作数之后。

在这里插入图片描述

前缀、中缀和后缀表达式

在这里插入图片描述

将中缀表达式转换为前缀和后缀表达式

这里规定:运算符只有+ - * /,操作数则被定义为任何大写字母A~Z或数字0~9。

代码如下:

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

use std::collections::HashMap;

fn main() {
    let infix = "( A + B ) * ( C + D )";
    let postfix = infix_to_postfix(infix);
    match postfix {
        Some(val) => {
            println!("{infix} -> {val}");
        },
        None => {
            println!("{infix} is not a correct infix string");
        }
    }

    let infix = "( 1 + 2 ) * 3";
    let postfix = infix_to_postfix(infix);
    match postfix {
        Some(val) => {
            println!("{infix} -> {val}");
        },
        None => {
            println!("{infix} is not a correct infix string");
        }
    }
}

// 中缀转后缀
fn infix_to_postfix(infix:&str) -> Option<String> {
    // 括号匹配检验
    if !par_checker3(infix) { return None;};

    // 设置各个运算符的优先级
    let mut prec = HashMap::new();
    //  HashMap<K, V> 类型储存了一个键类型 K 对应一个值类型 V 的映射。它通过一个 哈希函数(hashing function)来实现映射,决定如何将键和值放入内存中。
    prec.insert("(", 1);
    prec.insert(")", 1);
    prec.insert("+", 2);
    prec.insert("-", 2);
    prec.insert("*", 3);
    prec.insert("/", 3);
    
    // ops 用于保存运算符, postfix 用于保存后缀表达式
    let mut ops = Stack::new(); // 保存运算符
    let mut postfix = Vec::new(); // 保存后缀表达式
    for token in infix.split_whitespace() { // 按空格分割字符串切片
        // 将数字 0 - 9 和 大写字母 A - Z 入栈
        if ("A" <= token && token <= "Z") || ("0" <= token && token <= "9") {
            postfix.push(token); // 如果是字符或数字就存入 postfix
        }else if "(" == token {
            // 遇到开始符号,将运算符入栈
            ops.push(token); // 如果是开始括号运算符 ( 就存储 ops
        }else if ")" == token {
            // 比如 infix 为 (1 + 2) * 3,此时 postfix 类似为 [1,2],ops 类似为 [(,+]  
            // 遇到结束符号,将操作数入栈
            let mut top = ops.pop().unwrap(); // 如果是结束括号运算符 就去 ops 中匹配 对应的 开始 括号运算符 此时 ops 类似为 [(],因为 + 已经被 pop 出去了
            while top !="(" { // 如果匹配到的不是 ( 开始括号运算符,则说明是 + - * / 运算符 此时 top 比如为 +
                postfix.push(top); // 将 + - * / 运算符入栈至 postfix,比如 infix 为 (1 + 2) * 3,此时 postfix 类似为 [1,2,+]
                top = ops.pop().unwrap(); // 此时 ops 类似为 [],top = "(",到这里 while 就结束了 因为 top = "("
            }
            // 到这里时 ops 类似为 [],infix 为 (1 + 2) * 3 中的 () 就被丢弃了,而 postfix 已经为,如:[1,2,+]
        }else {
            // + - * / 运算符
            // 比较运算符的优先级以决定是否将运算符添加到 postfix 列表中
            while (!ops.is_empty()) && (prec[ops.peek().unwrap()] >= prec[token]) {
                postfix.push(ops.pop().unwrap()); // 比如 peek 是 *,而 当前 token 是 + ,则直接把 * 放入 postfix 
            }
            ops.push(token); // 此时将 * push 了进去
            // 此时 ops 如 [*]
        }
    }
    // 比如:infix 为 (1 + 2) * 3
    //  此时 ops 如 [*]
    // 此时 postfix 如 [1,2,+,3]
    // 将剩下的操作数入栈
    while !ops.is_empty() {
        postfix.push(ops.pop().unwrap()); // 此时 postfix 如 [1,2,+,3,*]
    }

    // 出栈并组成字符串
    let mut postfix_str = "".to_string();
    for c in postfix {
        postfix_str += &c.to_string();
        postfix_str += " "; // 加上空格
    }
    Some(postfix_str)
}

// 同时检测多种开始符号和结束符号是否匹配
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()
}

运行结果:

在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 将中缀表达式转换为后缀表达式是一个经典的问题,在Rust实现算法需要遵循以下步骤: 1. 创建一个,用于存储运算符和括号。 2. 从左到右扫描中缀表达式。 3. 如果遇到操作数(数字),则直接将其加入到后缀表达式中。 4. 如果遇到左括号,则将其压入中。 5. 如果遇到右括号,则将顶的运算符依次弹出并加入到后缀表达式中,直到遇到左括号为止。注意,左括号只弹出不加入后缀表达式中。 6. 如果遇到运算符,则将其压入中。但是,在压入之前需要先将中优先级大于等于该运算符的运算符弹出并加入到后缀表达式中,直到遇到优先级小于该运算符的运算符为止。 7. 当扫描完整个中缀表达式后,如果中还有运算符,依次弹出并加入到后缀表达式中。 以下是使用Rust实现算法的代码示例: ```rust fn infix_to_postfix(infix: &str) -> String { let mut stack = Vec::new(); let mut postfix = String::new(); let operators = "+-*/"; let mut iter = infix.chars().peekable(); while let Some(c) = iter.next() { if c.is_digit(10) { postfix.push(c); } else if c == '(' { stack.push(c); } else if c == ')' { while let Some(op) = stack.pop() { if op == '(' { break; } else { postfix.push(op); } } } else if operators.contains(c) { while let Some(op) = stack.last() { if *op == '(' { break; } else if operators.find(*op).unwrap() >= operators.find(c).unwrap() { postfix.push(stack.pop().unwrap()); } else { break; } } stack.push(c); } } while let Some(op) = stack.pop() { postfix.push(op); } postfix } ``` 该函数接受一个中缀表达式的字符串作为输入,返回一个后缀表达式的字符串作为输出。例如,如果输入为"(1+2)*3-4",则输出为"12+3*4-”。 ### 回答2: 要使用Rust实现中缀表达式后缀表达式,可以按照以下步骤进行: 1. 创建一个,用于存储运算符。 2. 遍历中缀表达式的每个字符。 3. 如果遇到操作数(数字),将其输出。 4. 如果遇到左括号"(",将其压入中。 5. 如果遇到右括号")",则将中的运算符依次弹出并输出,直到遇到左括号为止。 6. 对于遇到的运算符,如果为空,则将其压入中。 7. 如果不为空,比较顶运算符的优先级与当前运算符的优先级: - 如果当前运算符的优先级小于或等于顶运算符的优先级,则将顶运算符弹出并输出。 - 否则,将当前运算符压入中。 8. 遍历完所有字符后,将中的剩余运算符依次弹出并输出。 这样,通过按照上述步骤处理每个字符,就可以将中缀表达式转换为后缀表达式。 以下是一个用Rust实现中缀表达式后缀表达式的示例代码: ```rust fn infix_to_postfix(infix_expr: &str) -> String { let mut stack: Vec<char> = Vec::new(); let mut postfix_expr = String::new(); for c in infix_expr.chars() { match c { '0'..='9' => postfix_expr.push(c), '(' => stack.push(c), ')' => { while let Some(top) = stack.pop() { if top == '(' { break; } postfix_expr.push(top); } } '+' | '-' => { while let Some(top) = stack.last() { if *top == '(' { break; } postfix_expr.push(stack.pop().unwrap()); } stack.push(c); } '*' | '/' | '%' => { while let Some(top) = stack.last() { if *top == '(' || *top == '+' || *top == '-' { break; } postfix_expr.push(stack.pop().unwrap()); } stack.push(c); } _ => {} // ignore other characters } } while let Some(op) = stack.pop() { postfix_expr.push(op); } postfix_expr } fn main() { let infix_expr = "2+3*4"; let postfix_expr = infix_to_postfix(infix_expr); println!("Postfix Expression: {}", postfix_expr); // Output: "234*+" } ``` 在上述示例中,我们将操作数直接输出到后缀表达式字符串中,而运算符则通过来处理优先级。最后输出的后缀表达式为"234*+"。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

碳学长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值