ProtoBuf介绍
1 编码和解码
编写网络应用程序时,因为数据在网络传输的都是二进制字节码数据,在发送数据时进行编码,在接受数据时进行解码
codec(编码器)的组成部分有2个:decoder(解码器)和encoder(编码器)。encoder负责将数据转换为字节码数据,而decoder负责将字节码数据转为业务数据
2 Netty的编码和解码机制问题
Netty提供的编码器:
StringEncoder:对字符串数据进行编码
ObjectEncoder:对java对象进行编码
Netty提供的解码器:
StringDecoder:对字符串数据进行编码
ObjectDecoder:对java对象进行编码
Netty本身自带的ObjectDecoder和ObjectEncoder可以实现用来对JOPO对象或各种业务对象的编码和解码。底层使用的依然是Java序列化技术,而java序列化技术本身就存在问题
无法跨语言
序列化的体积太大,是二进制编码的5倍多
序列化的性能太低
所以就出现了Google的Protobuf。
3 ProtoBuf
3.1基本介绍:
1) ProtoBuf是Google发布的开源项目,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化,它很适合做数据存储或RPC[远程过程调用]数据交换格式,目前很多公司正在由http+json->tcp+protobuf
Protobuf是以message的方式来管理数据的
支持跨平台,跨语言,即客户端和服务端可以是不同语言编写的
高性能,可读性高
使用protobuf编译器能够自动生成代码,ProtoBuf是将类的定义使用.protobuf文件进行描述(注意:在idea中编写.proto文件时,会自动提示是否下载.protot编写插件,可以让语法高亮)
然后通过.protoc.exe编译器根据.protp自动生成java文件
3.2 ProtoBuf生成类(单种类型)
实例要求:客户端发送一个Student PoJo对象到服务器(通过ProtoBuf编码),服务端接收Student PoJo对象,并显示信息(通过ProtoBuf解码)
步骤一:在maven中引入ProtoBuf坐标,下载相关jar包
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.7.1</version>
</dependency>
步骤二:github下载相关protoc文件(注意:下载文件需和pom文件中版本对应,否则生成java文件时会报错)
下载链接:https://github.com/protocolbuffers/protobuf/tags
然后进行相关idea配置:https://blog.csdn.net/Xin_101/article/details/123332526?
步骤三:编写代码
服务端:
public class NettyServer {public static void main(String[] args) throws Exception {//1.创建2个线程组bossGroup和workerGroup//2 bossGroup只是处理连接请求,workerGroup真正的和客户端进行业务处理//3 两个都是无限循环NioEventLoopGroup bossGroup = new NioEventLoopGroup();NioEventLoopGroup workerGroup = new NioEventLoopGroup();try {//创建服务器端的启动对象,配置参数ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup,workerGroup)//设置两个线程组.channel(NioServerSocketChannel.class) //使用nioSocketChannel作为服务器的通道实现.option(ChannelOption.SO_BACKLOG,128)//设置线程队列得到连接.childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持活动连接状态.childHandler(new ChannelInitializer<SocketChannel>() {//给pipeline设置处理器@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//向pipeline加入ProtobufDecoder,指定对哪种对象进行解码ch.pipeline().addLast("decoder",new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));ch.pipeline().addLast(new NettyServerHandler());}});//给workerGroup的EventLoop对应的管道设置处理器System.out.println("....服务器 is ready...");//绑定一个端口并且同步,生成了一个ChannelFuture对象//启动服务器(并绑定端口)ChannelFuture cf = bootstrap.bind(6668).sync();//对关联通道进行监听cf.channel().closeFuture().sync();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}}
服务端处理器:
public class NettyServerHandler extends ChannelInboundHandlerAdapter {//读取数据的事件(这里读取客户端发送的消息)/**** @param ctx ChannelHandlerContext ctx:上下文对象,含有管道pipeline,通道channel,地址* @param msg 就是客户端发送的消息* @throws Exception*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//读取从客户端发送的StudentPojo,StudentStudentPOJO.Student student = (StudentPOJO.Student)msg;System.out.println("客户端发送的数据 id="+student.getId() + " 名字="+student.getName());}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.writeAndFlush(Unpooled.copiedBuffer(LocalDateTime.now()+" :hello,客户端~",CharsetUtil.UTF_8));}//处理异常,一般需要关闭通道@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}
客户端:
public class NettyClient {public static void main(String[] args) throws Exception{//客户端需要一个循环组NioEventLoopGroup group = new NioEventLoopGroup();try {//创建客户端的启动对象//注意客户端使用的是Bootstrap不是ServerBootstrapBootstrap bootstrap = new Bootstrap();bootstrap.group(group) //设置线程组.channel(NioSocketChannel.class) //设置客户端通道的实现类.handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//在pipeline中加入ProtobufEncoderch.pipeline().addLast("encoder",new ProtobufEncoder());ch.pipeline().addLast(new NettyClientHandler()); //加入自己的处理器}});System.out.println("客户端 ok ...");//启动客户端连接服务器 ChannelFuture涉及到netty的异步模型ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();//给关闭通道进行监听channelFuture.channel().closeFuture().sync();}finally {group.shutdownGracefully();}}}
客户端处理器:
public class NettyClientHandler extends ChannelInboundHandlerAdapter {//当通道就绪会触发该方法@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {//发送一个Student对象到服务器StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(4).setName("王五").build();ctx.writeAndFlush(student);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf)msg;System.out.println("服务器回复的消息:"+buf.toString(CharsetUtil.UTF_8));System.out.println("服务器的地址:"+ctx.channel().remoteAddress());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}}
如果不想在channelRead()方法中进行强转,可以将处理器实现SimpleChannelInboundHandler类型,从而实现channelRead0()方法

proto文件:
syntax = "proto3"; //版本
option java_outer_classname = "StudentPOJO"; //生成的外部类类名,同时也是文件名
//protobuf使用message管理数据
message Student{ //会在StudentPOJO 外部类生成一个内部类 Student
//他是真正发送的POJO对象int32 id = 1; //Student类中有一个属性id 类型为int32(protobuf类型) 注意: 1表示属性序号 不是值string name = 2;}
将proto文件转为java文件结果如下:

先启动客户端,再次启动服务端:
客户端控制台:

服务端控制台:

发现,已经用protobuf实现了数据的传输。
3.3 ProtoBuf生成类(多种类型)
实例要求:客户端发送一个Student PoJo \Worker PoJo对象到服务器(通过ProtoBuf编码),服务端接收Student PoJo\Worker PoJo对象,并显示信息(通过ProtoBuf解码)
服务端代码;
package com.liubujun.codec2;import com.liubujun.codec.StudentPOJO;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;/*** @Author: liubujun* @Date: 2023/2/15 10:24*/public class NettyServer {public static void main(String[] args) throws Exception {//1.创建2个线程组bossGroup和workerGroup//2 bossGroup只是处理连接请求,workerGroup真正的和客户端进行业务处理//3 两个都是无限循环NioEventLoopGroup bossGroup = new NioEventLoopGroup();NioEventLoopGroup workerGroup = new NioEventLoopGroup();try {//创建服务器端的启动对象,配置参数ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup,workerGroup)//设置两个线程组.channel(NioServerSocketChannel.class) //使用nioSocketChannel作为服务器的通道实现.option(ChannelOption.SO_BACKLOG,128)//设置线程队列得到连接.childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持活动连接状态.childHandler(new ChannelInitializer<SocketChannel>() {//给pipeline设置处理器@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//向pipeline加入ProtobufDecoder,指定对哪种对象进行解码ch.pipeline().addLast("decoder",new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance()));ch.pipeline().addLast(new NettyServerHandler());}});//给workerGroup的EventLoop对应的管道设置处理器System.out.println("....服务器 is ready...");//绑定一个端口并且同步,生成了一个ChannelFuture对象//启动服务器(并绑定端口)ChannelFuture cf = bootstrap.bind(6668).sync();//对关联通道进行监听cf.channel().closeFuture().sync();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}}
服务端处理器:
package com.liubujun.codec2;import com.liubujun.codec.StudentPOJO;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;import java.time.LocalDateTime;/*** @Author: liubujun* @Date: 2023/2/15 10:25*/public class NettyServerHandler extends SimpleChannelInboundHandler<MyDataInfo.MyMessage> {@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext, MyDataInfo.MyMessage msg) throws Exception {//根据dataType 来显示不同的信息MyDataInfo.MyMessage.DataType dataType = msg.getDataType();if (dataType == MyDataInfo.MyMessage.DataType.StudentType){MyDataInfo.Student student = msg.getStudent();System.out.println("学生id="+student.getId() +" 学生名字="+student.getName());}else if (dataType == MyDataInfo.MyMessage.DataType.WorkerType){MyDataInfo.Worker worker = msg.getWorker();System.out.println("工人年纪="+worker.getAge() +" 工人姓名="+worker.getName());}else {System.out.println("传输的类型不正确");}}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.writeAndFlush(Unpooled.copiedBuffer(LocalDateTime.now() + " :hello,客户端~", CharsetUtil.UTF_8));}//处理异常,一般需要关闭通道@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}
客户端:
package com.liubujun.codec2;import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufEncoder;/*** @Author: liubujun* @Date: 2023/2/15 10:27*/public class NettyClient {public static void main(String[] args) throws Exception{//客户端需要一个循环组NioEventLoopGroup group = new NioEventLoopGroup();try {//创建客户端的启动对象//注意客户端使用的是Bootstrap不是ServerBootstrapBootstrap bootstrap = new Bootstrap();bootstrap.group(group) //设置线程组.channel(NioSocketChannel.class) //设置客户端通道的实现类.handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//在pipeline中加入ProtobufEncoderch.pipeline().addLast("encoder",new ProtobufEncoder());ch.pipeline().addLast(new NettyClientHandler()); //加入自己的处理器}});System.out.println("客户端 ok ...");//启动客户端连接服务器 ChannelFuture涉及到netty的异步模型ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();//给关闭通道进行监听channelFuture.channel().closeFuture().sync();}finally {group.shutdownGracefully();}}}
客户端处理器:
package com.liubujun.codec2;import com.liubujun.codec.StudentPOJO;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;import java.util.Random;/*** @Author: liubujun* @Date: 2023/2/15 10:28*/public class NettyClientHandler extends ChannelInboundHandlerAdapter {//当通道就绪会触发该方法@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {//随机的发送Student 或者 Worker对象int random = new Random().nextInt(3);MyDataInfo.MyMessage myMessage = null;if (0 == random){ //发送Student对象myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.StudentType).setStudent(MyDataInfo.Student.newBuilder().setId(5).setName("小飞棍来喽").build()).build();}else { //发送一个Worker对象myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.WorkerType).setWorker(MyDataInfo.Worker.newBuilder().setAge(5).setName("来了 老李").build()).build();}ctx.writeAndFlush(myMessage);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf)msg;System.out.println("服务器回复的消息:"+buf.toString(CharsetUtil.UTF_8));System.out.println("服务器的地址:"+ctx.channel().remoteAddress());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}}
proto文件:
syntax = "proto3";
option optimize_for = SPEED; //加快解析
option java_package = "com.liubujun.codec2";//指定生成到哪个包下
option java_outer_classname = "MyDataInfo"; //外部类名称//proto可以使用message管理其它的message
message MyMessage {//定义一个枚举类型enum DataType {StudentType = 0; //在proto3中要求enum的编号从0开始WorkerType = 1;}//用data_type来标识传的是哪一个枚举类型DataType data_type = 1;//表示每次枚举类型最多只能出现其中一个,节省空间oneof dataBody{Student student = 2;Worker worker = 3;}
}message Student{int32 id = 1; //Studnet类的属性string name = 2;
}
message Worker{string name = 1;int32 age = 2;
}
先启动服务端,再启动多个客户端:发现数据正常传输

相关文章:

ProtoBuf介绍
1 编码和解码编写网络应用程序时,因为数据在网络传输的都是二进制字节码数据,在发送数据时进行编码,在接受数据时进行解码codec(编码器)的组成部分有2个:decoder(解码器)和encoder&a…...

数据结构:完全二叉树开胃菜小练习
目录 一.前言 二.完全二叉树的重要结构特点 三.完全二叉树开胃菜小练习 1.一个重要的数学结论 2.简单的小练习 一.前言 关于树及完全二叉树的基础概念(及树结点编号规则)参见:http://t.csdn.cn/imdrahttp://t.csdn.cn/imdra 完全二叉树是一种非常重要的数据结构: n个结点的…...

mybatis与jpa
1、官方文档 mybatis:mybatis-spring – jpa:https://springdoc.cn/spring-data-jpa/ 应用文档 jpa详解_java菜鸟1的博客-CSDN博客 JPA简介及其使用详解_Tourist-xl的博客-CSDN博客_jpa的作用 2、使用比较 mybatis一般用于互联网性质的项目&#x…...
js 求解《初级算法》66. 加一
一、题目描述 给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。 示例 1: 输入:…...

力扣-游戏玩法分析
大家好,我是空空star,本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目:511. 游戏玩法分析二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.其他总结…...
ZZNUOJ_用C语言编写程序实现1186 : 奖学金(结构体专题)(附完整源码)
题目描述 某校发放奖学金共5种,获取条件各不同: 1.阳明奖学金,每人8000,期末平均成绩>80,且在本学期发表论文大于等于1篇; 2.梨洲奖学金,每人4000,期末平均成绩>85,且班级评议成绩>80; 3.成绩优秀奖,每人2000,期末平均成绩>90; 4.西部奖学金,…...

加油站ai系统视频监测 yolov5
加油站ai系统视频监测通过yolov5网络模型深度学习边缘计算技术,加油站ai系统视频监测对现场卸油过程中人员违规离岗、现场灭火器没有按要求正确摆放、以及卸油前需要遵守静电释放15分钟、打电话、明火烟雾情况、抽烟行为进行自动识别。YOLO系列算法是一类典型的one-…...

【JDK8新特性之Stream流-Stream结果收集案例实操】
一.JDK8新特性之Stream流-Stream结果收集以及案例实操 二.Stream结果收集(collect函数)-实例实操 2.1 结果收集到集合中 /*** Stream将结果收集到集合中以及具体的实现 collect*/Testpublic void test01(){// 收集到List中 接口List<Integer> list Stream.of(1, 2, 3…...
Fiddler 抓包工具
HTTP代理所谓的http代理,其实就是代理客户机的http访问,主要代理浏览器访问页面。代理服务器是介于浏览器和web服务器之间的一台服务器,有了它之后,浏览器不是直接到Web服务器去取回网页而是向代理服务器发出请求,Requ…...

2023最新版网络安全保姆级指南,手把手带你从零基础进阶渗透攻防工程师
前言 一份网络攻防渗透测试的学习路线,不藏私了! 1、学习编程语言(phpmysqljshtml) 原因: phpmysql可以帮助你快速的理解B/S架构是怎样运行的,只有理解了他的运行原理才能够真正的找到问题/漏洞所在。所以对于国内那些上来就说…...

排序基础之选择排序法
目录 前言 一、什么是选择排序 二、实现选择排序 三、使用泛型扩展 四、使用自定义类型测试 前言 今天天气不错,这么好的天气不干点啥实在是有点可惜了,于是乎,拿出键盘撸一把! 来,今天来学习一下排序算法中的选…...

2.24测试用例
一.测试模型1.V模型特点:1.明确标注了测试的类型2.明确标注了测试阶段和开发阶段的对应关系缺点:测试后置2.W模型也叫双v模型,测试阶段全流程介入缺点:1.上一阶段完成.下一个阶段才能开始2.开发模型和测试模型也保持着一种线性的前后关系3.重文档,重过程,不支持敏捷模式二.设计…...
面试必刷101 Java题解 -- part 1
练习地址 面试必刷101-牛客1、链表反转2、链表内指定区间反转**3. 链表中的节点每k个一组翻转**4、**合并两个排序的链表**5、**合并k个已排序的链表**6、**判断链表中是否有环****7、链表中环的入口结点**8、链表中倒数最后k个结点**9、删除链表的倒数第n个节点****10、两个链…...

Python---关联与继承
专栏:python 个人主页:HaiFan. 专栏简介:Python在学,希望能够得到各位的支持!!! 关联与继承前言has a关联关系is a继承关系子类不添加__init__子类添加__init__前言 has a关联关系 has - a 是在…...

数据库行业的 “叛逆者”:大数据已“死”,MotherDuck 当立
“大数据”已死——现今我们最重要的事情不是担心数据大小,而是专注于我们将如何使用它来做出更好的决策。数据库行业发展至今,在数据层面有很多的加速和变革,尤其是过去几年的云数仓爆炸式增长,带来了行业的很多变化。毫无疑问&a…...

Linux->进程优先级
目录 1. 优先级的概念 2. 优先级的运作方式 3. Linux下查看进程优先级以及调整 3.1 查看进程优先级 3.2 修改进程优先级 1. 优先级的概念 1. cpu资源分配的先后顺序,就是指进程的优先权(priority)。 2. 优先权高的进程有优先执行权利。配…...

loki 日志管理的安装部署使用
loki介绍 Loki是 Grafana Labs 团队最新的开源项目,是一个水平可扩展,高可用性,多租户的日志聚合系统。它的设计非常经济高效且易于操作,因为它不会为日志内容编制索引,而是为每个日志流编制一组标签。 不对日志进行…...

CTFer成长之路之反序列化漏洞
反序列化漏洞CTF 1.访问url: http://91a5ef16-ff14-4e0d-a687-32bdb4f61ecf.node3.buuoj.cn/ 点击下载源码 本地搭建环境并访问url: http://127.0.0.1/www/public/ 构造payload: ?sindex/index/helloðanwhoamiPOST的参数&#…...

Python学习-----模块5.0(文件管理大师-->os模块)
目录 前言: 1.os.getcwd() 2. os.listdir(path) 3.os.walk(path) 4.os.path.exists(path) 5.os.mkdir(path) 6.os.makedirs(path,exist_okTrue) 7.os.rmdir(path) 8.os.remove(path) 9.os.path.join(p1,p2) 10.os.path.split(path) 11.os.path.isdi…...

第45届世界技能大赛“网络安全”赛项浙江省选拔赛竞赛任务书
第45届世界技能大赛浙江省选拔赛竞赛任务书 一、竞赛时间 8:00-17:00,共计9小时。 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 模块A 任务1 数据库安全加固 8:00-10:00 50 任务2 文件MD5校验 50 任务3 Linux系统服务渗透测试及安全加…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...

快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...

leetcode73-矩阵置零
leetcode 73 思路 记录 0 元素的位置:遍历整个矩阵,找出所有值为 0 的元素,并将它们的坐标记录在数组zeroPosition中置零操作:遍历记录的所有 0 元素位置,将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...