数据结构3、基于栈的后缀算术表达式求值

1 题目描述

图1 中缀表达式转化为后缀表达式题目描述
图2 基于栈的后缀算术表达式求值题目描述

2 题目解读

        借助一个运算符栈,可将中缀表达式转化为后缀表达式;借助一个运算数栈,可对后缀表达式求值。借助一个运算符栈和一个运算数栈,则可将中缀表达式转化为后缀表达式输出,并根据后缀表达式计算。

3 小题一:中缀表达式转化为后缀表达式

        借助一个运算符栈,则可以将中缀表达式转化为后缀表达式。

3.1 解题思路

        为实现中缀表达式转换成后缀表达式,可以使用一个工作栈OPTR寄存运算符,初始化为空栈;使用一个字符串Postfix寄存转换得到的后缀表达式,初始化为空串。具体步骤如下。

(1)初始化OPTR栈,将表达式起始符“#”压入OPTR栈。

(2)扫描表达式,读入第一个字符ch,如果表达式没有扫描完毕至“=”或OPTR的栈顶元素不为“#”时,则循环执行以下操作。

  • ch不是运算符,则加入字符串Postfix
  • ch是运算符,则根据OPTR的栈顶元素和ch的优先级比较结果,进行以下不同的处理。

        a.若是小于,则ch压入OPTR栈,读入下一字符ch

        b.若是大于,则弹出OPTR栈顶的运算符,加入字符串Postfix

        c.若是等于,则OPTR的栈顶元素是“(”且ch是“)”,这时弹出OPTR栈顶的“(”,相当于括号匹配成功,然后读入下一字符ch

(3)字符串Postfix中的元素即为后缀表达式,返回后缀表达式。

3.2 设计代码

#include <iostream>
#include <iomanip>
using namespace std;
//函数结果状态代码
#define OK 1
#define ERROR 0
#define OVERFLOW -2
//------顺序栈的存储结构------
#define MAXSIZE 100  //顺序栈存储空间的初始分配量
typedef char SElemType;
typedef struct
{
	SElemType *base; //栈底指针
	SElemType *top;  //栈顶指针
	int stacksize;   //栈可用的最大容量
}SqStack;
//Status是函数返回值类型,其值是函数结果状态代码
typedef int Status;
Status InitStack(SqStack &S);
Status Push(SqStack &S, SElemType e);
Status Pop(SqStack &S, SElemType &e);
SElemType GetTop(SqStack S);
//表达式求值相关算法
bool In(SElemType ch);
SElemType Precede(SElemType optr, SElemType ch);
//中缀表达式转化为后缀表达式
string ztoh(char ch);
int main() {
	char ch;
	while (true) {
		cin >> ch;
		if ('=' == ch) {
			break;
		}
		string res = ztoh(ch);
		cout << res << endl;
	}
	return 0;
}
string ztoh(char ch)
{
	SqStack OPTR;
	InitStack(OPTR);
	Push(OPTR, '#');
	string Postfix = "";
	//表达式未读完 或 OPTR栈有运算符
	while (ch != '=' || GetTop(OPTR) != '#')
	{
		if (!In(ch)) {
			Postfix.push_back(ch);
			cin >> ch;
		}
		else {
			switch (Precede(GetTop(OPTR), ch))
			{
			case '<':
				Push(OPTR, ch); cin >> ch;
				break;
			case '>':
				char theta;
				Pop(OPTR, theta);
				Postfix.push_back(theta);
				break;
			case '=':
				char x;
				Pop(OPTR, x); cin >> ch;
				break;
			}
		}
	}
	return Postfix;
}
//判定读入的字符ch是否为运算符的函数
bool In(SElemType ch)
{
	if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||
		ch == '(' || ch == ')' || ch == '=') {
		return true;
	}
	return false;
}
//判定运算符栈的栈顶元素与读入的运算符之间优先关系的函数
SElemType Precede(SElemType optr, SElemType ch)
{
	//规则(1)先乘除,后加减
	if ((optr == '+' || optr == '-') && (ch == '*' || ch == '/')) {
		return '<';
	}
	else if ((optr == '*' || optr == '/') && (ch == '+' || ch == '-')) {
		return '>';
	}
	//规则(2)从左算到右
	if (((optr == '+' || optr == '-') && (ch == '+' || ch == '-')) ||
		((optr == '*' || optr == '/') && (ch == '*' || ch == '/'))) {
		return '>';
	}
	//规则(3)先括号内,后括号外
	//optr不会出现右括号
	if (optr == '+' || optr == '-' || optr == '*' || optr == '/') {
		if (ch == '(') {
			return '<';
		}
		else if (ch == ')' || ch == '=') {
			return '>';
		}
	}
	else if (optr == '(') {
		if (ch == ')') {
			return '=';
		}
		else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(') {
			return '<';
		}
	}
	//optr中只有'#'的情况
	if (optr == '#' &&
		(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(')) {
		//ch肯定不是'='
		return '<';
	}
}
Status InitStack(SqStack &S)
{
	S.base = new SElemType[MAXSIZE];
	if (!S.base) exit(OVERFLOW);
	S.top = S.base;
	S.stacksize = MAXSIZE;
	return OK;
}
Status Push(SqStack &S, SElemType e)
{
	if (S.top - S.base == S.stacksize) return ERROR;
	*S.top++ = e;
	return OK;
}
Status Pop(SqStack &S, SElemType &e)
{
	if (S.top == S.base) return ERROR;
	e = *--S.top;
	return OK;
}
SElemType GetTop(SqStack S)
{
	if (S.top != S.base) {
		return *(S.top - 1);
	}
}

3.3 执行结果

图3 中缀表达式转化为后缀表达式代码执行结果

4 小题二:基于栈的后缀算术表达式求值

        借助一个运算数栈可对后缀表达式进行求值。

4.1 解题思路

        将中缀表达式转换为后缀表达式之后,对转换后得到的后缀表达式进行计算的具体步骤如下。

        借助一个工作栈OPND,用以寄存操作数或运算结果。从左到右扫描后缀表达式,读入第一个字符ch。若ch不是运算符,则压入OPND栈,读入下一字符;若ch是运算符,则从OPND栈中依次弹出两个数分别到YX,然后以“X ch Y”的形式计算出结果,将结果压入OPND栈中。如果后缀表达式未读完,重复执行上面过程,最后OPND栈顶元素即为表达式的求值结果,返回此元素。

4.2 设计代码

#include <iostream>
#include <iomanip>
using namespace std;
//函数结果状态代码
#define OK 1
#define ERROR 0
#define OVERFLOW -2
//Status是函数返回值类型,其值是函数结果状态代码
#define MAXSIZE 100 //顺序栈存储空间的初始分配量
typedef int Status;
typedef char SElemType;
typedef struct
{
	double *base; //栈底指针
	double *top;  //栈顶指针
	int stacksize;//栈可用的最大容量
}SqStack2;
Status InitStack2(SqStack2 &S);
Status Push2(SqStack2 &S, double e);
Status Pop2(SqStack2 &S, double &e);
double GetTop2(SqStack2 S);
//表达式求值相关函数
bool In(SElemType ch);
SElemType Precede(SElemType optr, SElemType ch);
double Operate(double x, SElemType theta, double y);
//计算后缀表达式
double calh(SElemType ch);
int main() {
	SElemType ch;
	while (true) {
		cin >> ch;
		if ('=' == ch) {
			break;
		}
		double res = calh(ch);
		//保留小数点后面2位
		cout << fixed << setprecision(2) << res << endl;
	}
	return 0;
}
double calh(SElemType ch)
{
	SqStack2 OPND;
	InitStack2(OPND);
	while (ch != '=')
	{
		if (!In(ch)) {
			//读入的数没有多位数、小数
			Push2(OPND, double(ch - 48));
		}
		else {
			double x, y;
			Pop2(OPND, y);
			Pop2(OPND, x);
			Push2(OPND, Operate(x, ch, y));
		}
		cin >> ch;
	}
	return GetTop2(OPND);
}
//判定读入的字符ch是否为运算符的函数
bool In(SElemType ch)
{
	if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||
		ch == '(' || ch == ')' || ch == '=') {
		return true;
	}
	return false;
}
//判定运算符栈的栈顶元素与读入的运算符之间优先关系的函数
SElemType Precede(SElemType optr, SElemType ch)
{
	//规则(1)先乘除,后加减
	if ((optr == '+' || optr == '-') && (ch == '*' || ch == '/')) {
		return '<';
	}
	else if ((optr == '*' || optr == '/') && (ch == '+' || ch == '-')) {
		return '>';
	}
	//规则(2)从左算到右
	if (((optr == '+' || optr == '-') && (ch == '+' || ch == '-')) ||
		((optr == '*' || optr == '/') && (ch == '*' || ch == '/'))) {
		return '>';
	}
	//规则(3)先括号内,后括号外
	//optr不会出现右括号
	if (optr == '+' || optr == '-' || optr == '*' || optr == '/') {
		if (ch == '(') {
			return '<';
		}
		else if (ch == ')' || ch == '=') {
			return '>';
		}
	}
	else if (optr == '(') {
		if (ch == ')') {
			return '=';
		}
		else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(') {
			return '<';
		}
	}
	//optr中只有'#'的情况
	if (optr == '#' &&
		(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(')) {
		//ch肯定不是'='
		return '<';
	}
}
//进行二元运算的函数
double Operate(double x, SElemType theta, double y)
{
	double z;
	switch (theta)
	{
	case '+':
		z = x + y;
		break;
	case '-':
		z = x - y;
		break;
	case '*':
		z = x * y;
		break;
	case '/':
		z = x / y;
		break;
	default:
		z = 0;
	}
	return z;
}
Status InitStack2(SqStack2 &S)
{
	S.base = new double[MAXSIZE];
	if (!S.base) exit(OVERFLOW);
	S.top = S.base;
	S.stacksize = MAXSIZE;
	return OK;
}
Status Push2(SqStack2 &S, double e)
{
	if (S.top - S.base == S.stacksize) return ERROR;
	*S.top++ = e;
	return OK;
}
Status Pop2(SqStack2 &S, double &e)
{
	if (S.top == S.base) return ERROR;
	e = *--S.top;
	return OK;
}
double GetTop2(SqStack2 S)
{
	if (S.top != S.base) {
		return *(S.top - 1);
	}
}

4.3 执行结果

图4 基于栈的后缀算术表达式求值代码执行结果

5 基于栈的后缀算术表达式求值实验

        在基于栈的后缀算术表达式求值实验中,借助栈,可以将中缀算术表达式转换为后缀表达式,并基于后缀表达式求值。

5.1 解题思路

        将小题一和小题二合并起来,则可完成基于栈的后缀算术表达式实验。

5.2 设计代码

#include <iostream>
#include <iomanip>
using namespace std;
//函数结果状态代码
#define OK 1
#define ERROR 0
#define OVERFLOW -2
//Status是函数返回值类型,其值是函数结果状态代码
#define MAXSIZE 100  //顺序栈存储空间的初始分配量
typedef int Status;
typedef struct
{
	char *base; //栈底指针
	char *top;  //栈顶指针
	int stacksize;   //栈可用的最大容量
}SqStack;
Status InitStack(SqStack &S);
Status Push(SqStack &S, char e);
Status Pop(SqStack &S, char &e);
char GetTop(SqStack S);
typedef struct
{
	double *base; //栈底指针
	double *top;  //栈顶指针
	int stacksize;//栈可用的最大容量
}SqStack2;
Status InitStack2(SqStack2 &S);
Status Push2(SqStack2 &S, double e);
Status Pop2(SqStack2 &S, double &e);
double GetTop2(SqStack2 S);
//表达式求值相关函数
bool In(char ch);
char Precede(char optr, char ch);
double Operate(double x, char theta, double y);
//中缀表达式 转 后缀表达式
string ztoh(char ch);
//计算后缀表达式
int calh(string res);
int main() {
	char ch;
	while (true) {
		cin >> ch;
		if ('=' == ch) {
			break;
		}
		string res = ztoh(ch);
		cout << res << endl;
		int r = calh(res);
		cout << r << endl;
	}
	return 0;
}
string ztoh(char ch)
{
	SqStack OPTR;
	InitStack(OPTR);
	Push(OPTR, '#');
	string Postfix = "";
	while (ch != '=' || GetTop(OPTR) != '#')
	{
		if (!In(ch)) {
			Postfix.push_back(ch);
			cin >> ch;
		}
		else {
			switch (Precede(GetTop(OPTR), ch))
			{
			case '<':
				Push(OPTR, ch); cin >> ch;
				break;
			case '>':
				char theta;
				Pop(OPTR, theta);
				Postfix.push_back(theta);
				break;
			case '=':
				char x;
				Pop(OPTR, x); cin >> ch;
				break;
			}
		}
	}
	return Postfix;
}
int calh(string res)
{
	SqStack2 OPND;
	InitStack2(OPND);
	int i = 0;
	char ch = res[i++];
	while (ch != '\0')
	{
		if (!In(ch)) {
			Push2(OPND, double(ch - 48));
		}
		else {
			double x, y;
			Pop2(OPND, y);
			Pop2(OPND, x);
			Push2(OPND, Operate(x, ch, y));
		}
		ch = res[i++];
		//cin >> ch;
	}
	return GetTop2(OPND);
}
//判定读入的字符ch是否为运算符的函数
bool In(char ch)
{
	if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||
		ch == '(' || ch == ')' || ch == '=') {
		return true;
	}
	return false;
}
//判定运算符栈的栈顶元素与读入的运算符之间优先关系的函数
char Precede(char optr, char ch)
{
	//规则(1)先乘除,后加减
	if ((optr == '+' || optr == '-') && (ch == '*' || ch == '/')) {
		return '<';
	}
	else if ((optr == '*' || optr == '/') && (ch == '+' || ch == '-')) {
		return '>';
	}
	//规则(2)从左算到右
	if (((optr == '+' || optr == '-') && (ch == '+' || ch == '-')) ||
		((optr == '*' || optr == '/') && (ch == '*' || ch == '/'))) {
		return '>';
	}
	//规则(3)先括号内,后括号外
	//optr不会出现右括号
	if (optr == '+' || optr == '-' || optr == '*' || optr == '/') {
		if (ch == '(') {
			return '<';
		}
		else if (ch == ')' || ch == '=') {
			return '>';
		}
	}
	else if (optr == '(') {
		if (ch == ')') {
			return '=';
		}
		else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(') {
			return '<';
		}
	}
	//optr中只有'#'的情况
	if (optr == '#' &&
		(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(')) {
		//ch肯定不是'='
		return '<';
	}
}
//进行二元运算的函数
double Operate(double x, char theta, double y)
{
	double z;
	switch (theta)
	{
	case '+':
		z = x + y;
		break;
	case '-':
		z = x - y;
		break;
	case '*':
		z = x * y;
		break;
	case '/':
		z = x / y;
		break;
	default:
		z = 0;
	}
	return z;
}
Status InitStack(SqStack &S)
{
	S.base = new char[MAXSIZE];
	if (!S.base) exit(OVERFLOW);
	S.top = S.base;
	S.stacksize = MAXSIZE;
	return OK;
}
Status Push(SqStack &S, char e)
{
	if (S.top - S.base == S.stacksize) return ERROR;
	*S.top++ = e;
	return OK;
}
Status Pop(SqStack &S, char &e)
{
	if (S.top == S.base) return ERROR;
	e = *--S.top;
	return OK;
}
char GetTop(SqStack S)
{
	if (S.top != S.base) {
		return *(S.top - 1);
	}
}
Status InitStack2(SqStack2 &S)
{
	S.base = new double[MAXSIZE];
	if (!S.base) exit(OVERFLOW);
	S.top = S.base;
	S.stacksize = MAXSIZE;
	return OK;
}
Status Push2(SqStack2 &S, double e)
{
	if (S.top - S.base == S.stacksize) return ERROR;
	*S.top++ = e;
	return OK;
}
Status Pop2(SqStack2 &S, double &e)
{
	if (S.top == S.base) return ERROR;
	e = *--S.top;
	return OK;
}
double GetTop2(SqStack2 S)
{
	if (S.top != S.base) {
		return *(S.top - 1);
	}
}

5.3 执行结果

图5 基于栈的后缀算术表达式求值实验代码执行结果

6 解题心得

  • 借助一个运算符栈,可以将中缀表达式转换为后缀表达式。
  • 借助一个运算数栈,可以对后缀表达式进行求值。
  • 基于栈的中缀算术表达式求值算法,和基于栈的后缀算术表达式求值算法,是两种重要的表达式求值算法。

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

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

相关文章

MongoDB安装以及卸载

查询id&#xff1a; docker ps [rootlocalhost ~]# docker stop c7a8c4ac9346 c7a8c4ac9346 [rootlocalhost ~]# docker rm c7a8c4ac9346 c7a8c4ac9346 [rootlocalhost ~]# docker rmi mongo sudo docker pull mongo:4.4 sudo docker images 卸载旧的 sudo docker stop mong…

Win10无法完成更新正在撤销更改的解决方法

在Win10电脑操作过程中&#xff0c;用户看到了“无法完成更新正在撤销更改”的错误提示&#xff0c;这样系统就不能成功完成更新&#xff0c;不知道如何操作才能解决此问题&#xff1f;以下小编分享最简单的解决方法&#xff0c;帮助大家轻松解决Win10电脑无法完成更新正在撤销…

BIO、NIO编程与直接内存、零拷贝

一、网络通信 1、什么是socket&#xff1f; Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层&#xff0c;它是一组接口&#xff0c;一般由操作 系统提供。客户端连接上一个服务端&#xff0c;就会在客户端中产生一个 socket 接口实例&#xff0c;服务端每接受 一个客户端…

【Linux网络编程】网络编程套接字(1)

【Linux网络编程】网络编程套接字(1) 目录 【Linux网络编程】网络编程套接字(1)源IP地址和目的IP地址端口号端口号和进程ID的关系 网络通信TCP协议UDP协议网络字节序socket编程接口简单的UDP网络程序 作者&#xff1a;爱写代码的刚子 时间&#xff1a;2024.1.29 前言&#xff1…

SV-7101T网络音频终端 网络对讲终端

SV-7101是一款IP网络广播终端&#xff0c;主要作为网络播放器使用&#xff0c;其接收网络的音频数据&#xff0c;提供音频输出。SV-7101与服务器主控软件、有源音箱配套使用可实现主控室对HG7101终端进行定时打铃、实时语音广播和紧急广播等功能。 淘宝速购&#xff1a; SV-701…

Android中属性property_get和property_set的详细用法介绍

1&#xff0c;property_get和property_set的作用说明 在Android操作系统中&#xff0c;property_get和property_set是用于获取和设置系统属性的函数。这些属性通常用于存储和读取配置信息&#xff0c;例如设备配置、网络设置、系统参数等。 property_get函数用于获取指定属性…

websocket 通信协议

websocket是什么 答: 它是一种网络通信协议&#xff0c;是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 意思就是服务器可以主动向客户端推送信息&#xff0c;客户端也可以主动向服务器发送信息 属于服务器推送技术的一种. 为什么需要websocket? 疑问?…

(五)MySQL的备份及恢复

1、MySQL日志管理 在数据库保存数据时&#xff0c;有时候不可避免会出现数据丢失或者被破坏&#xff0c;这样情况下&#xff0c;我们必须保证数据的安全性和完整性&#xff0c;就需要使用日志来查看或者恢复数据了 数据库中数据丢失或被破坏可能原因&#xff1a; 误删除数据…

MySQL原理(二)存储引擎(3)InnoDB

目录 一、概况&#xff1a; 1、介绍&#xff1a; 2、特点&#xff1a; 二、体系架构 1、后台线程 2、内存池&#xff08;缓冲池&#xff09; 三、物理结构 1、数据文件&#xff08;表数据和索引数据&#xff09; 1.1、作用&#xff1a; 1.2、共享表空间与独立表空间 …

【C/C++ 05】快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序算法&#xff0c;其基本思想是&#xff1a;任取待排序序列中的某元素作为基准值&#xff0c;按照该基准值将待排序集合分割成两个子序列&#xff0c;左子序列中所有元素均小于基准值&#xff0c;右子序列中所有元素均大于…

MySQL原理(二)存储引擎(1)概述

一、存储引擎介绍 1、概念&#xff1a; &#xff08;1&#xff09;MySQL中的数据用各种不下同的技术存储在文件中&#xff0c;每一种技术都使用不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力&#xff0c;这些不同的技术以及配套的功能在MySQL中称为存储引擎…

【数据结构与算法】7.详解队列的基本操作

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》|《数据结构与算法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢…

2024年最新 MySQL的下载、安装、启动与停止

一、MySQL的下载 MySQL最常用的2个版本&#xff1a; 社区版&#xff1a;免费开源&#xff0c;自由下载&#xff0c;不提供官方技术支持&#xff0c;大多数普通用户选择这个即可。企业版&#xff1a;需要付费&#xff0c;不能在线下载&#xff0c;可以使用30天&#xff0c;提供…

ctfshow web72

下载源码&#xff1a; 开启环境&#xff1a; 本题设置了 open_basedir()&#xff0c;将php所能打开的文件限制在指定的目录树中&#xff0c;包括文件本身。 因为 ini_set() 也被限制了&#xff0c;所以 open_basedir() 不能用 ini_set() 重新设置绕过。 使用 php 伪协议 glob:…

【网络】:网络套接字(UDP)

网络套接字 一.网络字节序二.端口号三.socket1.常见的API2.封装UdpSocket 四.地址转换函数 网络通信的本质就是进程间通信。 一.网络字节序 我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网…

数据结构----链表介绍、模拟实现链表、链表的使用

文章目录 1. ArrayList存在的问题2. 链表定义2.1 链表的概念及结构2.2 链表的组合类型 3. 链表的实现3.1 单向、不带头、非循环链表的实现3.2 双向、不带头节点、非循环链表的实现 4.LinkedList的使用4.1 什么是LinkedList4.2 LinkedList的使用4.2.1. LinkedList的构造4.2.2. L…

npm 淘宝镜像正式到期

由于node安装插件是从国外服务器下载&#xff0c;如果没有“特殊手法”&#xff0c;就可能会遇到下载速度慢、或其它异常问题。 所以如果npm的服务器在中国就好了&#xff0c;于是我们乐于分享的淘宝团队干了这事。你可以用此只读的淘宝服务代替官方版本&#xff0c;且同步频率…

Docker 数据管理、容器互联、网络与资源控制

一、docker数据管理 管理 Docker 容器中数据主要有两种方式&#xff1a;数据卷(Data volumes)和数据卷容器(Datavolumes containers)。 1、数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目录挂载到数据卷上&#xff0c;对数据卷的修改操作立…

seata 分布式

一、下载安装seata 已经下载好的朋友可以跳过这个步骤。这里下载的是seata1.6.1这个版本。 1、进入seata官网 地址&#xff1a; https://seata.io/zh-cn/index.html 2、进入下载 3、点击下载地址 下载地址&#xff1a; https://github.com/seata/seata 二、配置seata 进入c…

​ PaddleHub 首页图像 - 文字识别chinese_ocr_db_crnn_server​

PaddleHub 便捷地获取PaddlePaddle生态下的预训练模型&#xff0c;完成模型的管理和一键预测。配合使用Fine-tune API&#xff0c;可以基于大规模预训练模型快速完成迁移学习&#xff0c;让预训练模型能更好地服务于用户特定场景的应用 零基础快速开始WindowsLinuxMac Paddle…