C++之stack_queue扩展

1.反向迭代器

方向迭代器本质是一个适配器,使用模板实现,转递哪一个容器的迭代器就可以封装适配出对应的方向迭代器。

需要注意的是,operator*的实现,内部访问是迭代器当前位置的前一个位置,rend返回的是封装位置的迭代器的方向迭代器,这里是为了对称,所以解引用访问的是当前位置的前一个位置。

可以逐步调试理解。

代码实现

lterator.h文件

cpp 复制代码
#pragma once

template<class Iterator,class Ref,class Ptr>
struct ReverseIterator
{
	typedef ReverseIterator<Iterator, Ref, Ptr> Self;

	ReverseIterator(Iterator it)
		:_it(it)
	{}

	Ref operator*()
	{
		Iterator tmp = _it;
		--tmp;
		return *tmp;
	}

	Self& operator++()
	{
		--_it;
		return *this;
	}

	Self& operator--()
	{
		++_it;
		return *this;
	}
	bool operator!=(const Self& s)
	{
		return _it != s._it;
	}

	bool operator==(const Self& s)
	{
		return _it == s._it;
	}

	Iterator _it;
};

Lish.h文件

cpp 复制代码
#pragma once
#include<assert.h>

#include"Iterator.h"


namespace zym
{
	template<class T>
	struct list_node
	{
		T _data;
		list_node<T>* _next;
		list_node<T>* _prev;

		list_node(const T& data = T())
			:_data(data)
			, _next(nullptr)
			, _prev(nullptr)
		{}
	};

	template<class T, class Ref, class Ptr>
	struct list_iterator
	{
		typedef list_node<T> Node;
		typedef list_iterator<T, Ref, Ptr> Self;
		Node* _node;
		list_iterator(Node* node)
			:_node(node)
		{}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}

		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		Self operator++(int)
		{
			Self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		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;
		}

	};

	template<class T>
	class list
	{
		typedef list_node<T> Node;
	public:

		typedef list_iterator<T, T&, T*> iterator;
		typedef list_iterator<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;

		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());
		}
		iterator begin()
		{
			return _head->_next;
		}

		iterator end()
		{
			return _head;
		}

		const_iterator begin() const
		{
			return _head->_next;
		}

		const_iterator end() const
		{
			return _head;
		}
		void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}

		list()
		{
			empty_init();
		}

		list(initializer_list<T> il)
		{
			empty_init();
			for (auto& e : il)
			{
				push_back(e);
			}
		}

		list(const list<T>& lt)
		{
			empty_init();
			for (auto& e : lt)
			{
				push_back(e);
			}
		}


		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;

		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		void clear()
		{
			auto it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}
		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);

		}
		void push_back(const T& x)
		{
			insert(end(), x);
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);

			newnode->_next = cur;
			cur->_prev = newnode;
			newnode->_prev = prev;
			prev->_next = newnode;
			++_size;
			return newnode;
		}

		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());

			Node* prev = pos._node->_prev;
			Node* next = pos._node->_next;
			prev->_next = next;
			next->_prev = prev;
			--_size;
			return next;
		}

		size_t size() const
		{
			return _size;
		}

		bool empty() const
		{
			return _size == 0;
		}
		

	private:
		Node* _head;
		size_t _size;
	};
	
}

test.cpp文件

cpp 复制代码
#include<iostream>
#include<algorithm>
#include<list>
#include<vector>
using namespace std;
#include"Lish.h"

void test_list5()
{
	zym::list<int> lt;
	lt.push_back(1);
	lt.push_back(20);
	lt.push_back(3);
	lt.push_back(5);
	lt.push_back(5);
	lt.push_back(4);
	lt.push_back(5);
	lt.push_back(6);
	cout << *lt.begin() << endl;
	cout << *lt.end() << endl;
	cout << *lt.rend() << endl;
	cout << *lt.rbegin() << endl;
	cout << endl;
}

// 10:35
int main()
{
	//test_list6();
	//test_op2();

	test_list5();
	


	return 0;
}

2.计数器实现

逆波兰表达式

逆波兰表达式(Reverse Polish Notation,简称 RPN),也被称为后缀表达式,是一种数学表达式的书写方式,其中运算符位于其操作数之后,而不是像传统的中缀表达式那样位于操作数之间。这种表示方法的主要优点是它不需要括号来表示运算的顺序,从而简化了表达式的解析过程。

逆波兰表达式的特点

  1. 运算符后置:在逆波兰表达式中,运算符总是跟在其操作数之后。例如,中缀表达式 3 + 4 在逆波兰表示法中写作 3 4 +

  2. 无需括号:由于运算符的顺序是明确的(从左到右),因此不需要使用括号来指定运算顺序。这使得表达式的解析更加直观和简单。

  3. 运算符优先级:在逆波兰表达式中,乘法和除法的优先级高于加法和减法。如果表达式中包含多个相同优先级的运算符,它们从左到右依次计算。

代码实现

来自力扣题目

cpp 复制代码
class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> x;

        for(int i=0;i<tokens.size();i++)
        {
            string str=tokens[i];
            if(!(str=="+"||str=="-"||str=="*"||str=="/"))
            {
                x.push(stoi(str));
            }
            else
            {
                int right=x.top();
                x.pop();
                int left=x.top();
                x.pop();
                switch(str[0])
                {
                    case '+':
                        x.push(right+left);
                        break;
                    case '-':
                        x.push(left-right);
                        break;
                    case '*':
                        x.push(left*right);
                        break;
                    case '/':
                        x.push(left/right);
                        break;
                }
            }
        }
        return x.top();
    }
};

注意:switch要用char,每一个符号都对应各自的处理,stoi函数把字符串变成int,这里是把操作数和操作符都分开,遇到操作数就把操作数放到栈里面,然后碰到操作符就去栈顶去俩次,先出来的是右操作数(乘法和除法),后出来的左操作数,然后进行运算,结果在放回到栈里面。

中缀式转成后缀式

依次读取计算表达式中的值,遇到运算数直接输出。

建立一个栈存储运算符,利用栈后进先出的性质,遇到后面的运算符,出栈里面存的前面运算符进行比较,确定优先级。

遇到运算符,如果栈为空或者栈不为空且当前运算符比栈顶运算符高,就把运算符入栈。因为如果栈里面存储的是前一个运算符,当前运算符比前一个优先级高,说明前一个不能运算,当前运算符也不能运算,因为后面可能还有优先级更高的运算符。

遇到运算符,如果栈不为空且当前运算符比栈顶运算符优先级低或相等,说明栈顶的运算符可以运算了,则输出栈顶运算符,当前运算符继续走前面遇到运算符的逻辑。

遇到了(),就把括号里面的当成子表达式,进行递归处理子表达式,处理后转换出的后缀表达式加在前面表达式的后面即可。

代码实现

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
#include<set>
#include<map>
#include<string>
#include<vector>
#include<stack>
#include<assert.h>
using namespace std;

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] == ')')
			{
				// 子表达式结束
				// 输出栈里面的剩余运算符
				while (!st.empty())
				{
					v.push_back(string(1, st.top()));
					st.pop();
				}
				++i;
				return;
			}
			else
			{
				// 运算符 比较优先级
				if (st.empty() || operatorPrecedence(s[i]) > operatorPrecedence(st.top()))
				{
					st.push(s[i]);
					++i;
				}
				else
				{
					char op = st.top();
					st.pop();

					v.push_back(string(1, op));
				}
			}
		}

		// 表达式结束
		// 输出栈里面的剩余运算符
		while (!st.empty())
		{
			v.push_back(string(1, st.top()));
			st.pop();
		}
	}
	
};

int main()
{
	size_t i = 0;
	vector<string> v;
	//string str = "1+2-3";
	string str = "1+2-(3*4+5)-7";
	Solution().toRPN(str, i, v);
	for (auto& e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

例题

注意事项:

1.字符串存在空格

2.负号是减去的意思还是负数的意思,是负数就变为0-x的样子

3.第一个为 "-" 号

代码实现
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] == ')')
                {
                    // 子表达式结束
                    // 输出栈里面的剩余运算符
                    while (!st.empty())
                    {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                    ++i;
                    return;
                }
                else
                {
                    // 运算符
                    if (st.empty() || operatorPrecedence(s[i]) > operatorPrecedence(st.top()))
                    {
                        st.push(s[i]);
                        ++i;
                    }
                    else
                    {
                        char op = st.top();
                        st.pop();

                        v.push_back(string(1, op));
                    }
                }
            }

            // 表达式结束
            // 输出栈里面的剩余运算符
            while (!st.empty())
            {
                v.push_back(string(1, st.top()));
                st.pop();
            }
        }
        
    

    int evalRPN(vector<string>& tokens) {
        stack<int> x;

        for(int i=0;i<tokens.size();i++)
        {
            string str=tokens[i];
            if(!(str=="+"||str=="-"||str=="*"||str=="/"))
            {
                x.push(stoi(str));
            }
            else
            {
                int right=x.top();
                x.pop();
                int left=x.top();
                x.pop();
                switch(str[0])
                {
                    case '+':
                        x.push(right+left);
                        break;
                    case '-':
                        x.push(left-right);
                        break;
                    case '*':
                        x.push(left*right);
                        break;
                    case '/':
                        x.push(left/right);
                        break;
                }
            }
        }
        return x.top();
    }

    int calculate(string s) {
        size_t i=0;
        string num;
        vector<string> v;
        //去除空格
        for(auto e:s)
        {
            if(e!= ' ')
            {
                num+=e;
            }
            
        }
        swap(s,num);
        num.clear();

        for(size_t i=0;i<s.size();i++)
        {
            //解决注意事项的问题
            if(s[i]=='-'&&(i==0||(!isdigit(s[i-1])&&s[i-1]!=')')))
            {
                num+="0-";

            }
            else
            {
                num +=s[i];
            }
        }

        toRPN(num,i,v);
        return evalRPN(v);


    }
};
相关推荐
齐尹秦6 分钟前
什么是 HTML?
前端
计算机-秋大田9 分钟前
基于Spring Boot的小区疫情购物系统的设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·课程设计
loveking620 分钟前
SpringBoot调用华为云短信实现发短信功能
java·spring boot·华为云
啊吧怪不啊吧22 分钟前
C++相关基础概念之入门讲解(上)
c语言·开发语言·c++
uhakadotcom26 分钟前
Sentry:你的应用程序的守护者
前端·面试·github
李长渊哦27 分钟前
Spring Boot 约定大于配置:实现自定义配置
java·spring boot·后端
azaz_plus29 分钟前
C++ priority_queue 堆
开发语言·c++·stl··priority_queue
上官美丽30 分钟前
单一责任原则在Java设计模式中的深度解析
java·开发语言·设计模式
勘察加熊人31 分钟前
fastapi +angular迷宫求解可跨域
前端·fastapi·angular.js
橙序研工坊33 分钟前
Java基础语法练习42(基本绘图-基本的事件处理机制-小坦克的绘制-键盘控制坦克移动)
java·开发语言