C++学习之旅【C++拓展学习之反向迭代器实现、计算器实现以及逆波兰表达式】


🔥承渊政道: 个人主页
❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》

《C++知识内容》《Linux系统知识》

✨逆境不吐心中苦,顺境不忘来时路! 🎬 博主简介:

引言:前篇文章,小编已经介绍了关于C++Stack和Queue类的相关知识.接下来我将带领大家继续深入学习C++的相关内容!本篇文章着重介绍关于C++拓展学习之反向迭代器实现、计算器实现以及逆波兰表达式,那么这里面到底有哪些知识需要我们去学习的呢?废话不多说,带着这些疑问,下面跟着小编的节奏🎵一起学习吧!

目录

1. 反向迭代器思路(源码及框架分析)

反向迭代器是一种特殊的迭代器,它的核心作用是:从容器的末尾开始,逆向遍历容器中的元素(正常迭代器是从头部到尾部).可以把它理解成:
正常迭代器:像你从队伍第一个人走到最后一个人;
反向迭代器:像你从队伍最后一个人倒着走回第一个人.
SGI-STL30版本源代码,反向迭代器实现的核⼼源码在stl_iterator.h中,反向迭代器是⼀个适配器,各个容器中再适配出⾃⼰的反向迭代器.下⾯我们截出vector和list的的反向迭代器结构框架核⼼部分截取出来如下:

cpp 复制代码
// stl_list.h
template <class T, class Alloc = alloc>
class list {
public:
 typedef __list_iterator<T, T&, T*> iterator;
 typedef __list_iterator<T, const T&, const T*> const_iterator;
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
 typedef reverse_iterator<const_iterator> const_reverse_iterator;
 typedef reverse_iterator<iterator> reverse_iterator;
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
 typedef reverse_bidirectional_iterator<const_iterator, value_type,
 const_reference, difference_type> const_reverse_iterator;
 typedef reverse_bidirectional_iterator<iterator, value_type, reference,
 difference_type> reverse_iterator; 
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */
 iterator begin() { return (link_type)((*node).next); }
 const_iterator begin() const { return (link_type)((*node).next); }
 iterator end() { return node; }
 const_iterator end() const { return node; }
 reverse_iterator rbegin() { return reverse_iterator(end()); }
 const_reverse_iterator rbegin() const { return
const_reverse_iterator(end());}
 reverse_iterator rend() { return reverse_iterator(begin()); }
 const_reverse_iterator rend() const { return
const_reverse_iterator(begin());}
};

// stl_vector.h
template <class T, class Alloc = alloc>
class vector {
public:
 typedef T value_type;
 typedef value_type* iterator;
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
 typedef reverse_iterator<const_iterator> const_reverse_iterator;
 typedef reverse_iterator<iterator> reverse_iterator;
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
 typedef reverse_iterator<const_iterator, value_type, const_reference, 
 difference_type> const_reverse_iterator;
 typedef reverse_iterator<iterator, value_type, reference, difference_type>
 reverse_iterator;
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */
 iterator begin() { return start; }
 const_iterator begin() const { return start; }
 iterator end() { return finish; }
 const_iterator end() const { return finish; }
 reverse_iterator rbegin() { return reverse_iterator(end()); }
 const_reverse_iterator rbegin() const { return
const_reverse_iterator(end());}
 reverse_iterator rend() { return reverse_iterator(begin()); }
 const_reverse_iterator rend() const { return
const_reverse_iterator(begin());}
};

// stl_iterator.h
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
// This is the new version of reverse_iterator, as defined in the
// draft C++ standard. It relies on the iterator_traits template,
// which in turn relies on partial specialization. The class
// reverse_bidirectional_iterator is no longer part of the draft
// standard, but it is retained for backward compatibility.
template <class Iterator>
class reverse_iterator
{
protected:
 Iterator current;
public:
 typedef typename iterator_traits<Iterator>::iterator_category
 iterator_category;
 typedef typename iterator_traits<Iterator>::value_type
 value_type;
 typedef typename iterator_traits<Iterator>::difference_type
 difference_type;
 typedef typename iterator_traits<Iterator>::pointer
 pointer;
 typedef typename iterator_traits<Iterator>::reference
 reference;
 typedef Iterator iterator_type;
 typedef reverse_iterator<Iterator> self;
public:
 reverse_iterator() {}
 explicit reverse_iterator(iterator_type x) : current(x) {}
 reverse_iterator(const self& x) : current(x.current) {}
#ifdef __STL_MEMBER_TEMPLATES
 template <class Iter>
 reverse_iterator(const reverse_iterator<Iter>& x) : current(x.current) {}
#endif /* __STL_MEMBER_TEMPLATES */
 
 iterator_type base() const { return current; }
 reference operator*() const {
 Iterator tmp = current;
 return *--tmp;
 }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
 pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
 self& operator++() {
 --current;
 return *this;
 }
 self operator++(int) {
 self tmp = *this;
 --current;
 return tmp;
 }
 self& operator--() {
 ++current;
 return *this;
 }
 self operator--(int) {
 self tmp = *this;
 ++current;
 return tmp;
 }
 self operator+(difference_type n) const {
 return self(current - n);
 }
 self& operator+=(difference_type n) {
 current -= n;
 return *this;
 }
 self operator-(difference_type n) const {
 return self(current + n);
 }
 self& operator-=(difference_type n) {
 current += n;
 return *this;
 }
 reference operator[](difference_type n) const { return *(*this + n); } 
}; 
template <class Iterator>
inline bool operator==(const reverse_iterator<Iterator>& x, 
 const reverse_iterator<Iterator>& y) {
 return x.base() == y.base();
}
template <class Iterator>
inline bool operator<(const reverse_iterator<Iterator>& x, 
 const reverse_iterator<Iterator>& y) {
 return y.base() < x.base();
}
template <class Iterator>
inline typename reverse_iterator<Iterator>::difference_type
operator-(const reverse_iterator<Iterator>& x, 
 const reverse_iterator<Iterator>& y) {
 return y.base() - x.base();
}
template <class Iterator>
inline reverse_iterator<Iterator> 
operator+(reverse_iterator<Iterator>::difference_type n,
 const reverse_iterator<Iterator>& x) {
 return reverse_iterator<Iterator>(x.base() - n);
}
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
// This is the old version of reverse_iterator, as found in the original
// HP STL. It does not use partial specialization.
template <class BidirectionalIterator, class T, class Reference = T&, 
 class Distance = ptrdiff_t> 
class reverse_bidirectional_iterator {
typedef reverse_bidirectional_iterator<BidirectionalIterator, T, Reference,
 Distance> self;
protected:
 BidirectionalIterator current;
public:
 typedef bidirectional_iterator_tag iterator_category;
 typedef T value_type;
 typedef Distance difference_type;
 typedef T* pointer;
 typedef Reference reference;
 reverse_bidirectional_iterator() {}
 explicit reverse_bidirectional_iterator(BidirectionalIterator x)
 : current(x) {}
 BidirectionalIterator base() const { return current; }
 Reference operator*() const {
 BidirectionalIterator tmp = current;
 return *--tmp;
 }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
 pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
 self& operator++() {
 --current;
 return *this;
 }
 self operator++(int) {
 self tmp = *this;
 --current;
 return tmp;
 }
 self& operator--() {
 ++current;
 return *this;
 }
 self operator--(int) {
 self tmp = *this;
 ++current;
 return tmp;
 }
};
template <class RandomAccessIterator, class T, class Reference = T&,
 class Distance = ptrdiff_t> 
class reverse_iterator {
 typedef reverse_iterator<RandomAccessIterator, T, Reference, Distance>
 self;
 protected:
 RandomAccessIterator current;
public:
 typedef random_access_iterator_tag iterator_category;
 typedef T value_type;
 typedef Distance difference_type;
 typedef T* pointer;
 typedef Reference reference;
 reverse_iterator() {}
 explicit reverse_iterator(RandomAccessIterator x) : current(x) {}
 RandomAccessIterator base() const { return current; }
 Reference operator*() const { return *(current - 1); }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
 pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
 self& operator++() {
 --current;
 return *this;
 }
 self operator++(int) {
 self tmp = *this;
 --current;
 return tmp;
 }
 self& operator--() {
 ++current;
 return *this;
 }
 self operator--(int) {
 self tmp = *this;
 ++current;
 return tmp;
 }
 self operator+(Distance n) const {
 return self(current - n);
 }
 self& operator+=(Distance n) {
 current -= n;
 return *this;
 }
 self operator-(Distance n) const {
 return self(current + n);
 }
 self& operator-=(Distance n) {
 current += n;
 return *this;
 }
 Reference operator[](Distance n) const { return *(*this + n); }
};
#endif //__STL_CLASS_PARTIAL_SPECIALIZATION

1️⃣源码中我们可以看到reverse_iterator实现了两个版本,通过__STL_CLASS_PARTIAL_SPECIALIZATION 条件编译控制使⽤哪个版本,简单点说就是⽀持偏特化的迭代器萃取以后,反向迭代器使⽤的是这个版本template
class reverse_iterator; 之前使⽤的是
template <class BidirectionalIterator, class T, class Reference, class Distance>
class reverse_bidirectional_iterator;
template <class RandomAccessIterator, class T, class Reference class Distance>
class reverse_iterator;
2️⃣可以看到他们的差别主要是在模板参数是否传递迭代器指向的数据类型,⽀持偏特化的迭代器萃取以后就不需要给了,因为 reverse_iterator 内部可以通过迭代器萃取获取数据类型.迭代器萃取的本质是⼀个特化,这个还有些⼩复杂且不影响我们学习,这⾥我就不讲解了,有兴趣可以去看源码,这个我们主要使⽤模版参数传递数据类型的⽅式实现.
3️⃣反向迭代器本质是⼀个适配器,使⽤模版实现,传递哪个容器的迭代器就可以封装适配出对应的反向迭代器.因为反向迭代器的功能跟正向的迭代器功能⾼度相似,只是遍历的⽅向相反,类似operator++ 底层调⽤迭代器的 operator-- 等,所以封装⼀下就可以实现.
4️⃣⽐较奇怪的是operator*的实现,内部访问的是迭代器当前位置的前⼀个位置.这个要结合容器中rbegin和rend实现才能看懂,rbegin返回的是封装end位置的反向迭代器,rend返回的是封装begin位置迭代器的反向迭代器,这⾥是为了实现出⼀个对称,所以解引⽤访问的是当前位置的前⼀个位置.


2. 反向迭代器的实现

cpp 复制代码
// Common.h - 基础头文件和节点定义
#pragma once
#include <iostream>
#include <cstdlib>  // 内存分配
#include <algorithm> 
using namespace std;
// List节点定义(list依赖)
template<class T>
struct ListNode {
    T _data;
    ListNode<T>* _prev;
    ListNode<T>* _next;
    // 节点构造函数
    ListNode(const T& val = T())
        : _data(val), _prev(nullptr), _next(nullptr) {}
};
// 迭代器基础(list的正向迭代器)
template<class T, class Ref, class Ptr>
struct ListIterator {
    typedef ListNode<T> Node;
    typedef ListIterator<T, Ref, Ptr> Self;
    Node* _node;
    // 构造函数
    ListIterator(Node* node) : _node(node) {}
    // 运算符重载
    Ref operator*() const { return _node->_data; }
    Ptr operator->() const { return &_node->_data; }
    // 前置++
    Self& operator++() {
        _node = _node->_next;
        return *this;
    }
    // 后置++
    Self operator++(int) {
        Self tmp(*this);
        _node = _node->_next;
        return tmp;
    }
    // 前置--
    Self& operator--() {
        _node = _node->_prev;
        return *this;
    }
    // 后置--
    Self operator--(int) {
        Self tmp(*this);
        _node = _node->_prev;
        return tmp;
    }
    // 比较运算符
    bool operator==(const Self& s) const { return _node == s._node; }
    bool operator!=(const Self& s) const { return _node != s._node; }
};

// ReverseIterator.h
#pragma once
#include "Common.h"
namespace lcz {
template<class Iterator, class Ref, class Ptr>
struct ReverseIterator {
    typedef ReverseIterator<Iterator, Ref, Ptr> Self;
    // 存储正向迭代器
    Iterator _it;
    // 构造函数
    ReverseIterator(Iterator it) : _it(it) {}
    // 解引用:const版本+非const版本(保证const正确性)
    Ref operator*() {
        Iterator tmp = _it;
        return *(--tmp); // 反向迭代器解引用 = 正向迭代器退一位
    }
    Ref operator*() const {
        Iterator tmp = _it;
        return *(--tmp);
    }
    // 箭头运算符
    Ptr operator->() { return &(operator*()); }
    Ptr operator->() const { return &(operator*()); }
    // 前置++:反向迭代器++ → 正向迭代器--
    Self& operator++() {
        --_it;
        return *this;
    }
    // 后置++
    Self operator++(int) {
        Self tmp(*this);
        --_it;
        return tmp;
    }
    // 前置--:反向迭代器-- → 正向迭代器++
    Self& operator--() {
        ++_it;
        return *this;
    }
    // 后置--(修复原代码错误:--_it → ++_it)
    Self operator--(int) {
        Self tmp(*this);
        ++_it; // 反向迭代器后置-- = 正向迭代器后置++
        return tmp;
    }
    // 比较运算符
    bool operator!=(const Self& s) const {
        return _it != s._it;
    }
    bool operator==(const Self& s) const {
        return _it == s._it;
    }
};
} 

// vector.h
#pragma once
#include "ReverseIterator.h"
namespace lcz {
template<class T>
class vector {
public:
    // 正向迭代器
    typedef T* iterator;
    typedef const T* const_iterator;
    // 反向迭代器
    typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
    typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;
    // 构造函数
    vector() : _start(nullptr), _finish(nullptr), _endofstorage(nullptr) {}
    // 初始化列表构造(支持v = {1,2,3,4})
    vector(initializer_list<T> il) {
        size_t n = il.size();
        _start = new T[n];
        _finish = _start + n;
        _endofstorage = _finish;
        copy(il.begin(), il.end(), _start);
    }
    // 析构函数
    ~vector() {
        if (_start) {
            delete[] _start;
            _start = _finish = _endofstorage = nullptr;
        }
    }
    // 正向迭代器接口
    iterator begin() { return _start; }
    iterator end() { return _finish; }
    const_iterator begin() const { return _start; }
    const_iterator end() const { return _finish; }
    // 反向迭代器接口
    reverse_iterator rbegin() { return reverse_iterator(end()); }
    reverse_iterator rend() { return reverse_iterator(begin()); }
    const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
    const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
private:
    iterator _start = nullptr;      // 数组起始
    iterator _finish = nullptr;     // 有效数据末尾
    iterator _endofstorage = nullptr; // 容量末尾
};
} 

// list.h
#pragma once
#include "ReverseIterator.h"
namespace lcz {
template<class T>
class list {
    typedef ListNode<T> Node;
public:
    // 正向迭代器
    typedef ListIterator<T, T&, T*> iterator;
    typedef ListIterator<T, const T&, const T*> const_iterator;
    // 反向迭代器
    typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
    typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;
    // 构造函数:初始化头节点(双向循环链表)
    list() {
        _head = new Node;
        _head->_prev = _head;
        _head->_next = _head;
        _size = 0;
    }
    // 初始化列表构造(支持lt = {1,2,3,4})
    list(initializer_list<T> il) : list() {
        for (const auto& val : il) {
            push_back(val);
        }
    }
    // 析构函数
    ~list() {
        clear();
        delete _head;
        _head = nullptr;
        _size = 0;
    }
    // 尾插(初始化列表依赖)
    void push_back(const T& val) {
        Node* new_node = new Node(val);
        Node* tail = _head->_prev;
        tail->_next = new_node;
        new_node->_prev = tail;
        new_node->_next = _head;
        _head->_prev = new_node;
        _size++;
    }
    // 清空链表
    void clear() {
        iterator it = begin();
        while (it != end()) {
            it = erase(it);
        }
    }
    // 删除节点
    iterator erase(iterator pos) {
        Node* cur = pos._node;
        Node* prev = cur->_prev;
        Node* next = cur->_next;
        prev->_next = next;
        next->_prev = prev;
        delete cur;
        _size--;
        return iterator(next);
    }
    // 正向迭代器接口
    iterator begin() { return iterator(_head->_next); }
    iterator end() { return iterator(_head); }
    const_iterator begin() const { return const_iterator(_head->_next); }
    const_iterator end() const { return const_iterator(_head); }
    // 反向迭代器接口
    reverse_iterator rbegin() { return reverse_iterator(end()); }
    reverse_iterator rend() { return reverse_iterator(begin()); }
    const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
    const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
private:
    Node* _head = nullptr;
    size_t _size = 0;
};
} 

//test.cpp
#include "list.h"
#include "vector.h"
int main() {
    // 测试list反向迭代器
    cout << "list反向遍历:";
    lcz::list<int> lt = { 1,2,3,4 };
    lcz::list<int>::reverse_iterator rit = lt.rbegin();
    while (rit != lt.rend()) {
        cout << *rit << " "; // 输出:4 3 2 1
        ++rit;
    }
    cout << endl;
    // 测试vector反向迭代器
    cout << "vector反向遍历:";
    lcz::vector<int> v = { 1,2,3,4 };
    lcz::vector<int>::reverse_iterator vit = v.rbegin();
    while (vit != v.rend()) {
        cout << *vit << " "; // 输出:4 3 2 1
        ++vit;
    }
    cout << endl;
    return 0;
}

3. 逆波兰表达式介绍(也叫后缀表达式)

我们⽇常写的计算表达式都是中缀表达式,也就是运算符在中间,运算数在两边,但是直接读取⽆法⻢上进⾏运算因为⼀个计算表达式还涉及运算符优先级问题.如:1-2*(3-4)+5 中遇到-和*都⽆法运算,因为后⾯还有括号优先级更⾼.所以其中⼀种实现思路是把中缀表达式转换为后缀表达式,也就是说分析计算表达式的优先级,将运算符放到前⾯,运算符放到运算数的后⾯,然后我们依次读取后缀表达式,遇到运算符就可以进⾏运算了.后缀表达式也就做逆波兰表达式(Reverse Polish Notation, RPN),这种表示法由波兰逻辑学家J·卢卡西维兹于1929年提出,后来被⼴泛应⽤于计算机科学中.


3.1后缀表达式进⾏运算

后缀表达式因为已经确定好优先级,运算符⽅式⾮常简单,就是遇到运算符时,取前⾯的两个运算符进⾏运算,因为经过中缀转后缀优先级已经确定好了.建⽴⼀个栈存储运算数,读取后缀表达式,遇到运算数⼊栈,遇到运算符,出栈顶的两个数据进⾏运算,运算后将结果作为⼀个运算数⼊栈继续参与下⼀次的运算.读取表达式结束后,最后栈⾥⾯的值就是运算结果.


3.2逆波兰表达式求值

cpp 复制代码
//后缀表达式进⾏运算
class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        for(auto str : tokens)
        {
            if(str == "+" || str == "-" || str == "*" || str == "/")
            {
                // 运算符,运算,运算结果入栈
                int right = st.top();
                st.pop();

                int left = st.top();
                st.pop();

                switch(str[0])
                {
                    case '+':
                        st.push(left + right);
                        break;
                    case '-':
                        st.push(left - right);
                        break;
                    case '*':
                        st.push(left * right);
                        break;
                    case '/':
                        st.push(left / right);
                        break;
                    default:
                        break;
                }
            }
            else
            {
                // 运算数入栈
                st.push(stoi(str));
            }
        }
        return st.top();
    }
};

3.3中缀表达式转后缀表达式(含代码实现)

1️⃣依次读取计算表达式中的值,遇到运算数直接输出.
2️⃣建⽴⼀个栈存储运算符,利⽤栈后进新出性质,遇到后⾯运算符,出栈⾥⾯存的前⾯运算符进⾏⽐较,确定优先级.
3️⃣遇到运算符,如果栈为空或者栈不为空且当前运算符⽐栈顶运算符优先级⾼,则当前运算符⼊栈.因为如果栈⾥⾯存储的是前⼀个运算符,当前运算符⽐前⼀个优先级⾼,说明前⼀个不能运算,当前运算符也不能运算,因为后⾯可能还有更⾼优先级的运算符.
4️⃣遇到运算符,如果栈不为为空且当前运算符⽐栈顶运算符优先级低或相等,说明栈顶的运算符可以运算了,则输出栈顶运算符,当前运算符继续⾛前⾯遇到运算符的逻辑.
5️⃣如果遇到(),则把括号的计算表达式当成⼀个⼦表达式,跟上思路类似,进⾏递归处理⼦表达式,处理后转换出的后缀表达式加在前⾯表达式的后⾯即可.
6️⃣计算表达式或者()中⼦表达式结束值,输出栈中所有运算符.

cpp 复制代码
#include <iostream>
#include <stack>
#include <vector>
#include <string>
#include <cctype>
#include <cassert>
using namespace std;
class Solution {
public:
    // 获取运算符优先级
    int operatorPrecedence(char ch) {
        static const pair<char, int> opPD[] = { {'+', 1}, {'-', 1}, {'*', 2}, {'/', 2} };
        int n = sizeof(opPD) / sizeof(opPD[0]);
        for (int i = 0; i < n; ++i) {
            if (opPD[i].first == ch) {
                return opPD[i].second;
            }
        }
        assert(false && "非法运算符");
        return -1;
    }
    // 中缀转逆波兰表达式(核心函数,栈作为参数传递,避免递归时重建)
    void toRPN(const string& s, size_t& i, vector<string>& rpn, stack<char>& opStack) {
        while (i < s.size()) {
            if (isdigit(s[i])) {
                // 处理多位数(操作数直接加入结果)
                string num;
                while (i < s.size() && isdigit(s[i])) {
                    num += s[i];
                    ++i;
                }
                rpn.push_back(num);
            }
            else {
                if (s[i] == '(') {
                    // 左括号入栈,递归处理括号内子表达式
                    opStack.push(s[i]);
                    ++i;
                    toRPN(s, i, rpn, opStack);
                }
                else if (s[i] == ')') {
                    // 右括号:弹出运算符直到左括号(左括号弹出但不加入结果)
                    ++i;
                    while (!opStack.empty() && opStack.top() != '(') {
                        rpn.push_back(string(1, opStack.top()));
                        opStack.pop();
                    }
                    // 弹出左括号(不加入结果)
                    if (!opStack.empty()) {
                        opStack.pop();
                    }
                    // 结束当前递归层,回到外层处理
                    return;
                }
                else {
                    // 处理运算符:优先级判断
                    // 栈非空 + 栈顶不是左括号 + 当前运算符优先级 ≤ 栈顶 → 弹出栈顶运算符
                    while (!opStack.empty() && opStack.top() != '(' 
                           && operatorPrecedence(s[i]) <= operatorPrecedence(opStack.top())) {
                        rpn.push_back(string(1, opStack.top()));
                        opStack.pop();
                    }
                    // 当前运算符入栈,i递增(关键:避免死循环)
                    opStack.push(s[i]);
                    ++i;
                }
            }
        }
    }

    // 封装接口:对外提供简洁的调用方式
    vector<string> convertToRPN(const string& s) {
        vector<string> rpn;
        stack<char> opStack;
        size_t i = 0;
        toRPN(s, i, rpn, opStack);
        // 遍历结束后,弹出栈中剩余的运算符
        while (!opStack.empty()) {
            rpn.push_back(string(1, opStack.top()));
            opStack.pop();
        }
        return rpn;
    }
    // 逆波兰表达式求值(可选:验证转换结果是否正确)
    int evalRPN(const vector<string>& rpn) {
        stack<int> st;
        for (const string& token : rpn) {
            if (isdigit(token[0]) || (token.size() > 1 && token[0] == '-' && isdigit(token[1]))) {
                // 处理数字(含负数)
                st.push(stoi(token));
            }
            else {
                // 处理运算符
                int b = st.top(); st.pop();
                int a = st.top(); st.pop();
                int res = 0;
                if (token == "+") res = a + b;
                else if (token == "-") res = a - b;
                else if (token == "*") res = a * b;
                else if (token == "/") res = a / b; // 整数除法向零取整
                st.push(res);
            }
        }
        return st.top();
    }
};
int main() {
    Solution sol;
    // 测试用例1:简单表达式
    string str1 = "1+2-3";
    vector<string> rpn1 = sol.convertToRPN(str1);
    cout << "中缀表达式:" << str1 << endl;
    cout << "逆波兰表达式:";
    for (const auto& e : rpn1) {
        cout << e << " "; // 输出:1 2 + 3 -
    }
    cout << endl;
    cout << "计算结果:" << sol.evalRPN(rpn1) << endl; // 输出:0
    cout << "------------------------" << endl;

    // 测试用例2:带括号的复杂表达式
    string str2 = "1+2-(3*4+5)-7";
    vector<string> rpn2 = sol.convertToRPN(str2);
    cout << "中缀表达式:" << str2 << endl;
    cout << "逆波兰表达式:";
    for (const auto& e : rpn2) {
        cout << e << " "; // 输出:1 2 + 3 4 * 5 + - 7 -
    }
    cout << endl;
    cout << "计算结果:" << sol.evalRPN(rpn2) << endl; // 输出:1+2=3; 3*4=12+5=17; 3-17=-14; -14-7=-21
    return 0;
}

4.基本计算器的实现


有了上⾯两个部分计算器OJ的⼤部分问题就解决了,但是这⾥还有⼀些问题需要处理.因为OJ中给的中缀表达式是字符串,字符串中包含空格,需要去掉空格.其次就是负数和减号,要进⾏区分,将所有的负数-x转换为0-x.

cpp 复制代码
class Solution {
public:
 int operatorPrecedence(char ch)
 {
 struct opPD
 {
 char _op;
 int _pd;
 };
 static opPD arr[] = { {'+', 1},{'-', 1},{'*', 2},{'/', 2} };
 for (auto& e : arr)
 {
 if (e._op == ch)
 {
 return e._pd;
 }
 }
 assert(false);
 return -1;
 }
 void toRPN(const string& s, size_t& i, vector<string>& v)
 {
 stack<char> st;
 while (i < s.size())
 {
 if (isdigit(s[i]))
 {
 // 运算数输出
 string num;
 while (i < s.size() && isdigit(s[i]))
 {
 num += s[i];
 ++i;
 }
 v.push_back(num);
 }
 else
 {
 if (s[i] == '(')
 {
 // 递归⽅式处理括号中的⼦表达式
 ++i;
 toRPN(s, i, v);
 }
 else if (s[i] == ')')
 {
 ++i;
 // 栈中的运算符全部输出
 while (!st.empty())
 {
 v.push_back(string(1, st.top()));
 st.pop();
 }
 // 结束递归
 return;
 }
 else
 {
 // 运算符
 // 1、如果栈为空或者栈不为空且当前运算符⽐栈顶运算符优先级⾼,则当前运算符⼊栈
 // 2、如果栈不为空且⽐栈顶运算符优先级低或相等,说明栈顶的运算符可以运算了,输出栈顶运算符,当前运算符继续⾛前⾯遇到运算符的逻辑
 if (st.empty() || operatorPrecedence(s[i]) > operatorPrecedence(st.top()))
 {
 st.push(s[i]);
 ++i;
 }
 else
 {
 v.push_back(string(1, st.top()));
 st.pop();
 }
 }
 }
 }
 // 栈中的运算符全部输出
 while (!st.empty())
 {
 v.push_back(string(1, st.top()));
 st.pop();
 }
 }
long long evalRPN(const vector<string>& tokens) {
 stack<long long> s;
 for (size_t i = 0; i < tokens.size(); ++i)
 {
 const string& str = tokens[i];
 // str为数字
 if (!("+" == str || "-" == str || "*" == str || "/" == str))
 {
 s.push(stoll(str));
 }
 else
 {
 // str为操作符
 long long right = s.top();
 s.pop();
 long long left = s.top();
 s.pop();
 switch (str[0])
 {
 case '+':
 s.push(left + right);
 break;
 case '-':
 s.push(left - right);
 break;
 case '*':
 s.push(left * right);
 break;
 case '/':
 s.push(left / right);
 break;
 }
 }
 }
 return s.top();
 }
 int calculate(string s)
 {
 // 1、去除所有空格,否则下⾯的⼀些逻辑没办法处理
 std::string news;
 news.reserve(s.size());
 for (auto ch : s)
 {
 if (ch != ' ')
 news += ch;
 }
 s.swap(news);
 news.clear();
 // 2、将所有的负数-x转换为0-x
 for (size_t i = 0; i < s.size(); ++i)
 {
 if (s[i] == '-' && (i == 0 || (!isdigit(s[i - 1]) && s[i - 1] != ')')))
 news += "0-";
 else
 news += s[i];
 }
 // 中缀表达式转成后缀表达式
 size_t i = 0;
 vector<string> v;
 toRPN(news, i, v);
 // 后缀表达式进⾏运算
 return evalRPN(v);
 }
 };

解题思路
1️⃣表达式预处理
去除空格 :首先去除表达式中的所有空格,简化后续处理逻辑.
负数特殊处理 :对于出现在表达式开头或左括号后的负号,需要特殊处理.例如,将 "-x" 转换为 "0-x",但为了避免溢出,我们直接将整个负数作为一个完整的数字解析(如 "-2147483648" 作为一个整体).
2️⃣中缀表达式转后缀表达式(逆波兰表达式)
使用递归下降法 :由于括号会改变运算顺序,我们使用递归的方式处理括号内的子表达式.具体地,遇到左括号时递归处理,直到遇到右括号返回.
运算符优先级 :设置加减为1级,乘除为2级.在转换过程中,如果当前运算符优先级高于栈顶运算符,则入栈;否则将栈顶运算符弹出并加入后缀表达式,直到栈空或遇到更低优先级的运算符.
数字直接输出 :遇到数字时,将连续的数字字符拼接成一个完整的数字字符串,直接加入后缀表达式.
3️⃣后缀表达式求值
使用栈模拟 :遍历后缀表达式,遇到数字则压栈,遇到运算符则弹出栈顶两个数字进行运算,并将结果压栈.
处理大数 :由于可能涉及边界值(如 -2147483648),使用 long long 类型进行计算,避免溢出.最后将结果转换为 int 返回.


敬请期待下一篇文章内容-->C++模板相关内容!


相关推荐
froginwe112 小时前
SQLite 表达式详解
开发语言
泰勒疯狂展开2 小时前
Vue3研学-组件的生命周期
开发语言·前端·vue
爱吃泡芙的小白白2 小时前
使用Cursor来进行连接SSH远程主机中出现的问题(自用)
服务器·学习·ssh·cursor
froginwe112 小时前
JSON 语法详解
开发语言
XYCMS2 小时前
PHP 外贸网站标题怎么用英文分割生成带杠号“-”的短网址
开发语言·php
云游云记2 小时前
php设计模式总结
开发语言·设计模式·php
●VON2 小时前
Flutter for OpenHarmony:基于选择模式状态机与原子批量更新的 TodoList 批量操作子系统实现
学习·flutter·ui·openharmony·von
王老师青少年编程2 小时前
信奥赛C++提高组csp-s知识详解及案例实践(汇总版)
c++·知识·csp·高频考点·信奥赛·csp-s·提高组
川西胖墩墩2 小时前
教育智能化:自适应学习与知识图谱构建
人工智能·学习·知识图谱