Netty-NIO

文章目录

  • 一、NIO-Selector
    • 1.处理accept
    • 2.cancel
    • 3.处理read
    • 4.处理客户端断开
    • 5. 处理消息的边界
    • 6. 写入内容过多的问题
    • 7. 处理可写事件
  • 二、多线程优化
  • 三、NIO概念剖析
    • 1. stream 和 channel
    • 2. IO模型
      • 2.1 阻塞IO
      • 2.2 非阻塞IO
      • 2.3多路复用
      • 2.4 同步异步
    • 3. 零拷贝
      • 3.1 NIO优化
      • 3.2 sendFile优化
      • 3.3 进一步优化
    • 4. AIO(异步IO)


一、NIO-Selector

1.处理accept

//1.创建selector,管理多个channel
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocate(16);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
//SelectionKey就是将来事件发生后,通过它可以知道事件和哪个channel的事件
//四个事件:
//accept 会在有连接请求时触发
//connect 是客户端,连接建立后触发
//read 可读事件
//write 可写事件
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
while(true){//3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行selector.select();//4.处理事件,selectedKeys内部包含了所有发生的事件Iterator<SelectionKey> iter = selector.selectedKeys.iterator();while(iter.next()){SelectionKey key = iter.next();ServerSocketChannel channel = (ServerSocketChannel)key.channel();SocketChannel sc = channel.accept();}
}

2.cancel

//1.创建selector,管理多个channel
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocate(16);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
while(true){//3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行//select在事件未处理时,它不会阻塞,事件发生后要么处理,要么取消,不能置之不理selector.select();//4.处理事件,selectedKeys内部包含了所有发生的事件Iterator<SelectionKey> iter = selector.selectedKeys.iterator();while(iter.next()){SelectionKey key = iter.next();key.cancel();}
}

3.处理read

用完key必须要remove

//1.创建selector,管理多个channel
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocate(16);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
while(true){//3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行selector.select();//4.处理事件,selectedKeys内部包含了所有发生的事件//selector会在发生事件后,向集合中加入key,但不会删除Iterator<SelectionKey> iter = selector.selectedKeys.iterator();while(iter.next()){SelectionKey key = iter.next();//处理key时,要从selectedKeys集合中删除,否则下次处理就会有问题iter.remove();//5.区分事件类型if(key.isAcceptable()){ //如果是acceptServerSocketChannel channel = (ServerSocketChannel)key.channel();SocketChannel sc = channel.accept();sc.configureBlocking(false);SelectionKey sckey = sc.register(selector, 0, null);scKey.interestOps(SelectionKey.OP_READ);}elseif(key.isReadable()){//拿到触发事件的channelServerSocketChannel channel = (ServerSocketChannel)key.channel();ByteBuffer buffer = ByteBuffer.allocate(16);channel.read(buffer);buffer.flip();debugRead(buffer);}}
}

4.处理客户端断开

//1.创建selector,管理多个channel
Selector selector = Selector.open(); 
ByteBuffer buffer = ByteBuffer.allocate(16); 
ServerSocketChannel ssc = ServerSocketChannel.open(); 
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
SelectionKey sscKey = ssc.register(selector, 0, null); sscKey.interestOps(SelectionKey.OP_ACCEPT); 
ssc.bind(new InetSocketAddress(8080)); 
while(true){ //3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行 selector.select(); //4.处理事件,selectedKeys内部包含了所有发生的事件 //selector会在发生事件后,向集合中加入key,但不会删除 Iterator<SelectionKey> iter = selector.selectedKeys.iterator(); while(iter.next()){ 		SelectionKey key = iter.next(); //处理key时,要从selectedKeys集合中删除,否则下次处理就会有问题 iter.remove(); //5.区分事件类型 if(key.isAcceptable()){ //如果是accept ServerSocketChannel channel = (ServerSocketChannel)key.channel(); SocketChannel sc = channel.accept();sc.configureBlocking(false); SelectionKey sckey = sc.register(selector, 0, null); scKey.interestOps(SelectionKey.OP_READ); }elseif(key.isReadable()){ try{ //拿到触发事件的channel ServerSocketChannel channel = (ServerSocketChannel)key.channel(); ByteBuffer buffer = ByteBuffer.allocate(16); int read = channel.read(buffer);//如果是正常断开,read的方法的返回值是-1 if(read == -1){ key.cancel(); }else{ buffer.flip(); debugRead(buffer); } }catch(IOException e){ e.printStackTrace();//因为客户端断开了,因此需要将key取消(从selector 的keys集合中真正删除key) key.cancel();}}}
}

5. 处理消息的边界

  1. 固定消息长度,数据包大小一样,服务器按预定长度读取,缺点是浪费带宽
  2. 按分隔符拆分,缺点是效率低
  3. TLV格式,Type类型,Length长度,Value数据,可以方便获取消息大小,分配合适的buffer,缺点是buffer需要提前分配,如果内容过大,影响server吞吐量
    • Http1.1是TLV格式
    • Http2.0是LTV格式
private static void split(ByteBuffer source){source.flip();for(int i = 0; i < source.limit(); i++){//找到一条完整消息if(source.get(i) == '\n'){int length = i + 1 -source.position();//把这条完整消息存入新的ByteBufferByteBuffer target = ByteBuffer.allocate(length);//从source读,向target写for(int j = 0; j < length; j++){target.put(source.get());}debugAll(target);}}source.compact();
}public static void main(){//1.创建selector,管理多个channelSelector selector = Selector.open(); ByteBuffer buffer = ByteBuffer.allocate(16); ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false);//2.建立selector和channel的联系(注册)SelectionKey sscKey = ssc.register(selector, 0, null); 		sscKey.interestOps(SelectionKey.OP_ACCEPT); ssc.bind(new InetSocketAddress(8080)); while(true){ //3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行 selector.select(); //4.处理事件,selectedKeys内部包含了所有发生的事件 //selector会在发生事件后,向集合中加入key,但不会删除 Iterator<SelectionKey> iter = selector.selectedKeys.iterator(); while(iter.next()){ 		SelectionKey key = iter.next(); //处理key时,要从selectedKeys集合中删除,否则下次处理就会有问题 iter.remove(); //5.区分事件类型 if(key.isAcceptable()){ //如果是accept ServerSocketChannel channel = (ServerSocketChannel)key.channel(); SocketChannel sc = channel.accept();sc.configureBlocking(false); ByteBuffer buffer = ByteBuffer.allocate(16); //attachment附件//将一个byteBuffer作为附件关联到selectionKey上SelectionKey sckey = sc.register(selector, 0, buffer); scKey.interestOps(SelectionKey.OP_READ); }elseif(key.isReadable()){ try{ //拿到触发事件的channel ServerSocketChannel channel = (ServerSocketChannel)key.channel(); //获取selectionKey上关联的附件ByteBuffer buffer = (ByteBuffer)key.attatchment();int read = channel.read(buffer);//如果是正常断开,read的方法的返回值是-1 if(read == -1){ key.cancel(); }else{ split(buffer);if(buffer.position() == buffer.limit()){//扩容ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity()*2);buffer.flip();newBuffer.put(buffer);//复制key.attach(newbuffer);//替换掉key上原有的buffer}} }catch(IOException e){ e.printStackTrace();//因为客户端断开了,因此需要将key取消(从selector 的keys集合中真正删除key) key.cancel();}}}}
}

6. 写入内容过多的问题

//服务器
public static void main(){ServerSocketChannel ssc = ServerSocketChannrl.open();ssc.configureBlocking(false);Selector selector = Selector.open();ssc.register(selector, SelectionKey.OP_ACCEPT);ssc.bind(new InetSocketAddress(8080));while(trye){selector.select();Iterator<SelectionKey> iter = selector.selectedKeys.iterator();while(iter.hasNext()){SelectionKey key = iter.next();iter.remove();if(key.isAcceptable()){SocketChannel sc = ssc.accept();sc.configureBlocking(false);//1.向客户端发送大量数据StringBuilder sb = new StringBuilder();for(int i = 0; i < 3000000; i++){sb.append("a");}BytrBuffer buffer = Charset.defaultCharset().encode(sb.toString());//不符合非阻塞模式while(buffer.hasRemaining()){//2.返回值代表实际写入的字节数//不能一次性写完//write == 0 缓冲区满,写不了int write = sc.write(buffer);System.out.println(write):}}}}
}//客户端
public static void main(){SocketChannel sc = SocketChannel.open();sc.connect(new InetSocketAddress("localhost",8080));//3.接收数据int count = 0;while(true){ByteBuffer buffer = ByteBuffer.allocate(1024*1024);count += sc.read(buffer);System.out.println(count);buffer.clear();}
}

7. 处理可写事件

//服务器
public static void main(){ServerSocketChannel ssc = ServerSocketChannrl.open();ssc.configureBlocking(false);Selector selector = Selector.open();ssc.register(selector, SelectionKey.OP_ACCEPT);ssc.bind(new InetSocketAddress(8080));while(trye){selector.select();Iterator<SelectionKey> iter = selector.selectedKeys.iterator();while(iter.hasNext()){SelectionKey key = iter.next();iter.remove();if(key.isAcceptable()){SocketChannel sc = ssc.accept();sc.configureBlocking(false);SelectionKey sckey = sc.register(selector, 0, null);sckey.interestOps(SelectionKey.OP_READ);//1.向客户端发送大量数据StringBuilder sb = new StringBuilder();for(int i = 0; i < 3000000; i++){sb.append("a");}BytrBuffer buffer = Charset.defaultCharset().encode(sb.toString());//2.返回值代表实际写入的字节数//不能一次性写完//先写一次int write = sc.write(buffer);System.out.println(write)://3.判断是否有剩余内容while(buffer.hasRemaining()){//4.关注可写事件sckey.interestOps(sckey.interestOps() + SelectionKey.OP_WRITE);//sckey.interestOps(sckey.interestOps() | SelectionKey.OP_WRITE);//5.把未写完的数据挂到sckey上sckey.attach(buffer);}}elseif(key.isWritable())[ByteBuffer buffer = (ByteBuffer) key.attachment();SocketChannel sc = (SocketChannel)key.channel();int write = sc.write(buffer);System.out.println(write)://6.清理操作,内存释放if!buffer.haeRemaining()){key.attach(null);//需要清除bufferkey.interestOps(key.interestOps() - SelectionKey.OP_WRITE);//不需关注可写事件}}}}
}

二、多线程优化

前面的代码只有一个选择器,没有充分利用多核cpu,如何改进呢?
分两组选择器:(boss建立连接,worker负责数据读写)

  • 单线程配一个选择器,专门处理accept事件
  • 创建cpu核心数的线程,每个线程配一个选择器,轮流处理read事件
public static void main(){Thread.currentThrea().setName("boss");ServerSocketChannel ssc = ServerSocketChannel.open();ssc.configuraBlocking(flase);Selector boss = Selector.open();SelectionKey bosskey = ssc.register(boss, 0, null);bosskey.interestOps(SelectionKey.OP_ACCEPT):ssc.bind(new InetSocketAddress(8080));//1.创建固定数量的worker并初始化Worker[] workers = new Worker[2];for(int i = 0; i < workers.length; i++{workers[i] = new Worker("worker-"+i);}//计数器AtomicInteger index = new AtomicInteger():while(true){boss.select();Iterator<SelectionKey> iter = boss.selectedKeys().iterator();while(iter.hasNext()){SelectionKey key = iter.next();iter.remove();if(key.isAcceptable())[SocketChannel sc = ssc.accept();sc.configureBlocking(false)://2.关联selector//轮询workers[index.getAndIncrement() % workers.length}.register(sc);}}}
}static class Worker implements Runnable{private Thread thread;private Selector worker;private String name;private volatile boolean star = false;//还未初始化private ConcurrentLinkedQueue<Runnable> queue = new ConcurrentLinkedQueue<>():public Worker(String name){this.name = name;}//初始化线程和selectorpublic void register(SocketChannel sc){if!start){selector = Selector.open();thread = new Thread(this, name);thread.start():start = true;}//向队列添加任务,但这个任务并没有被boss立刻执行queue.add()->{try{sc.register(worker.selector, SelectionKey.OP_READ, null);}catch(ClosedChannelException e) {e.printStackTrace()}}//唤醒run()中的select方法selector.wakeup();//也可以用以下方式,先wakeup,后select阻塞时也能被唤醒/* selector.wakeup();sc.register(worker.selector, SelectionKey.OP_READ, null);*/}@Overridepublic void run(){while(true){try{worker.select();Runnable task = queue.poll();if(task != null){task.run();}Iterator<SelectionKey> iter = worker.selectedKeys().iterator();while(iter.hasNext()){SlectionKey key = iter.next();iter.remove();if(key.isReadable()){ByteBuffer buffer = ByteBuffer.allocate(16);SocketChannel channel = (SocketChannel)key.channel();channel.read(buffer);buffer.flip();debugAll(buffer);}}}catch(IOException e){e.printStackTrace();}}}
}

三、NIO概念剖析

1. stream 和 channel

  • stream不会自动缓冲数据,channel会利用系统提供的发送缓冲区、接收缓冲区(更底层)
  • stream仅支持阻塞API,channel同时支持阻塞、非阻塞API,网络channel可配合selector实现多路复用
  • 二者均为全双工,即读写可同时进行

2. IO模型

2.1 阻塞IO

在这里插入图片描述
用户线程被阻塞(同步)

2.2 非阻塞IO

在这里插入图片描述
read是中运行,无数据立刻返回,有数据复制完返回
等待数据非阻塞,复制数据阻塞(同步)
缺点:多测内核切换

2.3多路复用

在这里插入图片描述
select等待数据阻塞,read复制数据阻塞(同步)
阻塞IO
多路复用
一次性处理多个channel上的事件

2.4 同步异步

同步:线程自己去获取结果(一个线程)
异步:一个线程发送,一个线程送结果(两个线程)
异步非阻塞
read非阻塞

异步阻塞不存在

3. 零拷贝

传统io将一个文件通过socket写出,内部工作流程:
在这里插入图片描述
用户和内核态的切换发生了3次,这个操作比较重量级
数据拷贝了4次

3.1 NIO优化

通过DirectByteBuffer
ByteBuffer.allocate(10) ,返回HeapByteBuffer,使用Java内存
ByteBuffer.allocateDirect(10),返回DirectByteBuffer,使用操作系统内存
在这里插入图片描述
java可以使用DirectByteBuffer将堆内存映射到jvm内存中来直接访问使用
减少了一次数据拷贝,用户态与内核态的切换次数没有减少

3.2 sendFile优化

Linux2.1后提供sendFile方法,Java中对应着两个channel调用transferTo/transferFrom方法拷贝数据
在这里插入图片描述
只发生了一次用户态与内核态的切换
数据拷贝了3次

3.3 进一步优化

在这里插入图片描述
一次切换,2次拷贝

零拷贝,并不是真正无拷贝,而是在不会拷贝重复数据到jvm内存中,零拷贝的优点有:

  • 更少的用户态和内核态切换
  • 不利用cpu计算,减少cpu缓存伪共享
  • 零拷贝适合小文件传输

4. AIO(异步IO)

netty不支持异步IO

public static void main(){try(AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths.get("data.txt"), StandardOpenOption.READ)){//参数1 ByteBuffer//读取的起始位置//附件//回调对象 CompletionHandlerByteBuffer buffer =ByteBuffer.allocate(16);channel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>(){@Override//read成功public void completed(Integer result, ByteBuffer attachment){attachment.flip();debugAll(attachment);}@Override// read失败public void failed(Throwable exc, ByteBuffer attachment){exc.printStachTrace();}}):}catch(IOException e){e.printStackTrace();}//主线程结束,守护线程结束//接收控制台的输入,控制台不输入,就停在这儿System.in.read();
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 面试题 04.01. 节点间通路

文章目录 一、题目二、C# 题解 一、题目 节点间通路。给定有向图&#xff0c;设计一个算法&#xff0c;找出两个节点之间是否存在一条路径。 点击此处跳转题目。 示例1: 输入&#xff1a; n 3, graph [[0, 1], [0, 2], [1, 2], [1, 2]], start 0, target 2 输出&#xff1…

点云从入门到精通技术详解100篇-三维点云帧间编码

目录 前言 国内外研究现状 几何信息编码研究现状 属性信息研究编码现状

hive葵花宝典:hive函数大全

文章目录 版权声明函数1 函数分类2 查看函数列表3 数学函数取整函数: round指定精度取整函数: round向下取整函数: floor向上取整函数: ceil取随机数函数: rand幂运算函数: pow绝对值函数: abs 4 字符串函数字符串长度函数&#xff1a;length字符串反转函数&#xff1a;reverse…

autoware.ai感知随笔--地面滤波

autwoware.ai中点云预处理–points_preprocessor points_preprocessor cloud_transformer: 点云坐标转换,将输入的点云转化为velodyne坐标系下的点云。 compare_map_filter: 对比激光雷达点云和点云地图&#xff0c;然后提取&#xff08;或去除&#xff09;一致的点。 |input_…

机器学习实战-系列教程7:SVM分类实战2线性SVM(鸢尾花数据集/软间隔/线性SVM/非线性SVM/scikit-learn框架)项目实战、代码解读

&#x1f308;&#x1f308;&#x1f308;机器学习 实战系列 总目录 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 SVM分类实战1之简单SVM分类 SVM分类实战2线性SVM SVM分类实战3非线性SVM 3、不同软间隔C值 3.1 数据标准化的影响 如图左边是没…

css 左右宽固定,中间自适应——双飞翼布局

最近面试的时候遇到一个提问说&#xff0c;如何做到一个左右宽度固定&#xff0c;中间自适应的布局&#xff0c;我的答案不重要&#xff0c;重要的是不是面试官想听到的答案&#xff0c;这样问大概率他想听到的答案一定是双飞翼布局&#xff0c;所以今天就手敲一个双飞翼布局让…

ES-索引管理

前言 数据类型 ​ 搜索引擎是对数据的检索&#xff0c;所以我们先从生活中的数据说起。我们生活中的数据总体分为两种&#xff1a; 结构化数据非结构化数据 结构化数据&#xff1a; 也称作行数据&#xff0c;是由二维表结构来逻辑表达和实现的数据&#xff0c;严格地遵循数…

智能合约编写高级篇(二)区块哈希介绍

本文档从区块哈希基本概念出发&#xff0c;详细介绍了中移链的区块哈希交易接口和应用方向。适用于EOS区块链智能合约高级开发人员&#xff0c;熟悉如何获取当前发生交易所在的区块号和区块哈希前缀&#xff0c;并通过Tapos机制验证交易的有效性。 01 概述 &#xff08;一&…

【大模型】更强的开源可商用的中英文大语言模型baichuan2来了,从零开始搭建

【大模型】更强的开源可商用的中英文大语言模型baichuan2来了&#xff0c;从零开始搭建 Baichuan 2 介绍技术报告github 地址 模型下载开放协议协议 测试评估通用领域测试7B 模型结果13B 模型结果 法律、医疗7B 模型结果13B 模型结果 数学、代码7B 模型结果13B 模型结果 多语言…

html 学习 之 文本标签

下面是一些常见的HTML文本标签&#xff08;&#xff0c;&#xff0c;&#xff0c;&#xff0c;和&#xff09;以及它们的作用&#xff1a; 标签 (Emphasis - 强调): 作用&#xff1a;用于在文本中表示强调或重要性。 示例&#xff1a; <p>这是一段文本&#xff0c;&l…

数据资产管理:数据目录怎么搞?

经过了站在业务视角的自上而下的数据梳理&#xff0c;以及站在IT视角的自下而上的数据盘点&#xff0c;一套“热腾腾”的数据资产清单终于新鲜出炉了。 通过数据资产盘点&#xff0c;企业终于知道他们拥有哪些数据、如何使用数据、是否安全以及数据在哪里。 然而&#xff0c;据…

微博一面:JVM预热,你的方案是啥?

说在前面 在40岁老架构师 尼恩的读者社区(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如微博、阿里、汽车之家、极兔、有赞、希音、百度、网易、滴滴的面试资格&#xff0c;遇到一几个很重要的面试题&#xff1a; JVM预热&#xff0c;你的方案是啥&#xff1f;Springb…

docker 和 podman的区别

Podman 和 Docker 都是用于容器化应用程序的工具&#xff0c;它们在很多方面非常相似&#xff0c;但也有一些关键区别&#xff1a; 1. 架构和权限&#xff1a; - Docker&#xff1a;Docker 使用守护进程&#xff08;dockerd&#xff09;来管理容器&#xff0c;它需要在操作…

TortoiseGit设置作者信息和用户名、密码存储

前言 Git 客户端每次与服务器交互&#xff0c;都需要输入密码&#xff0c;但是我们可以配置保存密码&#xff0c;只需要输入一次&#xff0c;就不再需要输入密码。 操作说明 在任意文件夹下&#xff0c;空白处&#xff0c;鼠标右键点击 在弹出菜单中按照下图点击 依次点击下…

侧边栏的文章分类、热门文章和热门文章的展示(Go 搭建 qiucode.cn 之九)

早就有言,秋码记录 虽早已不是原来的面貌,但这终究是不防碍我们使用golang来搭建它。 而又为什么是使用golang呢?并非是其他编程语言呢?想必 时候回答【我为什么要学习 Go 语言(golang)】这个问题了 已经给出了答案! 当然,当初学习golang时,不单单是为了搭建一个博客应…

【数据结构】二叉树基础入门

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

makefile之目标文件生成

目标文件:源码经过编译还没有链接那些中间文件.linux .o文件 gcc $(CFLAGS) -c xxx.c -o xx.o include Makefile.config SRC : $(wildcard *.c wildcard ./audio_module/*.c) SRC_OBJ $(patsubst %.c,%.o,$(SRC))all:$(SRC_OBJ) $(info contents $(SRC))$(info objfiles $(SR…

获取板块分类并展示

板块分类也会变动&#xff0c;偶尔看下&#xff0c;利于总体分析大盘 https:dapanyuntu.com/ 该网站含有板块信息 分析接口 搜素关键字 拷贝curl到curl解析工具&#xff0c;去掉无用的参数&#xff0c;生成requests代码 尝试nginx反代接口 server {listen 443;loca…

数据结构算法-分而治之算法

引言 在茫茫人海中找寻那个特定的身影&#xff0c;犹如在浩瀚的星海中寻找那一颗独特的星辰。小森&#xff0c;一个平凡而真实的男孩&#xff0c;此时正在人群中寻找他的朋友&#xff0c;温迪。 小森运用了一种“分而治之”的算法策略&#xff0c;将周围的人群分成两组&#…

算法通关村第十九关——动态规划是怎么回事(青铜)

算法通关村第十九关——动态规划是怎么回事&#xff08;青铜&#xff09; 前言1 什么是动态规划2 动态规划的解题步骤3 简单入门3.1 组合总和3.2 最小路径和3.3 三角形最小路径和 4 理解动态规划 前言 动态规划是一种解决复杂问题的算法思想&#xff0c;它将一个大问题分解为多…