海南公司网站建设哪家快/企业seo网站推广
- 个人主页:北·海
- 🎐CSDN新晋作者
- 🎉欢迎 👍点赞✍评论⭐收藏
- ✨收录专栏:C/C++
- 🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!🤗
天天酷跑,一款童年游戏,主要是进行跳跃操作,和躲避障碍物,中篇主要实现人物的下蹲,随机障碍物的生成以及优化main函数里面的sleep(30)
一. 游戏的展示效果
二.本节开发日志
上篇已更新 : 天天酷跑上篇
优化main函数里面的sleep(30);
1.利用接口getDelay()函数
2.更新窗口标志update;
3.优化来自用户点击时候的消息
实现玩家的下蹲
1利用计数器
随机障碍物的实现
1.障碍物池子
2.随机数取类型
三.优化上篇中main函数里的sleep(30)
首先为什么要优化这个呢?
了解sleep的都知道,这个是让程序休眠30ms也就是当程序执行到这里的时候,会在这里呆30ms,因为这款游戏需要接收玩家的按键消息,所以有时候玩家在这30ms内有键盘响应的时候,由于程序休眠,无法接收到消息,会降低游戏的体验性
要解决的问题: 在按跳跃键的时候不受sleep的影响
解决方法 : 利用时间戳,定义一个计时器,当达到该计时器的设定的时间就可以打开刷新窗口的按钮,或者当跳跃时候打开该按钮,代码如下:
int main() {init();while (1) {keyEvent();timer += getDelay();if (timer >= 30) {timer = 0;update = true;}if (update) {update = false;BeginBatchDraw();//渲染背景updataBg();//实现人物的奔跑putimagePNG2(heroX, heroY, &imgHero[heroIndex]);//渲染障碍物updateEnemy();EndBatchDraw();fly();}}system("pause");return 0; }
代码解释 : timer是一个全局变量,用于累加两次函数执行的时间,当达到30ms就会将update标志设置为true进行刷新界面
时间戳 : 一个用于表示特定时刻的数值,通常是一个整数或浮点数。在上述代码中,时间戳用于记录函数调用的时间点
这样优化的话,感觉和sleep(30)的效果一样,此时还需要在跳跃的时候将其update设为true,在以后只要接受玩家键盘消息的时候都要加上
void jump() {//跳跃只需要改变y值即可,在底层数据管理函数实现,此时只需要给出可以改数据的信号即可heroJump = true;update = true; }
这样的话就解决了玩家按跳跃键程序休眠的问题 ,此时提高了游戏的体验性
开发之前,先回顾一下上篇中的开发流程,将开发分为两层,一个是渲染层(update)一个是数据层(fly),渲染层是将游戏的图片呈现出来,数据层是控制渲染层的,实现游戏的控制,其他的功能根据可封装为函数围绕这两层进行展开,将游戏的资源加载放在初始化函数(init)
四.实现玩家的下蹲功能
天天酷跑主要就是跳跃和下蹲,下蹲可躲避障碍物柱子,给障碍物的创建先奠定基础,实现下蹲和实现跳跃的流程大致相似,分析可知,下蹲时玩家发出的信号,那么我们就可用从用户点击函数开始开发,当玩家按下a的时候执行下蹲操作
资源必须先加载进来,因为下蹲有两张图片,所以在全局定义一个存放该图片的数组,然后再初始化中进行加载
//下蹲 IMAGE imgDown[2]; bool heroDown;//下蹲标志void init(){ ...//人物的下蹲 loadimage(&imgDown[0], "res/d1.png"); loadimage(&imgDown[1], "res/d2.png"); heroIndex = 0; heroDown = false; }
由于下蹲是个动态的,所以要用到帧序列,也要将其初始化为0
用户点击
void keyEvent() {//获取玩家键盘事件char ch = 0;if (_kbhit()) {ch = _getch();if (ch == ' ') {//空格为跳跃jump();}else if (ch == 'a') {//a为下蹲down();}} }
当程序收到下蹲的消息时,就会执行该下蹲操作,将下蹲功能封装为一个函数
实现下蹲
void down() { //下蹲只需要改变y值即可, 在底层数据更新函数实现, 此时只需要给出可以改数据的信号即可heroDown = true;update = true;heroIndex = 0 ; }
此时定义一个下蹲的标志,如果时true的话,就可以在数据层进行修改数据,然后打开更新界面的按钮
现在就可以在数据层实现数据的更新了
数据更新
//实现跳跃if (heroJump) {//跳跃状态if (heroY < jumpHeightMax) {heroJumpOff = 4;//+ (-4)等于向上走,+4等于向下走}heroY += heroJumpOff;if (heroY > 345 - imgHero[0].getheight()) {//达到地面heroJump = false;heroJumpOff = -4;}}else {//改变人物帧序列heroIndex = (heroIndex + 1) % 12;}
这里是上篇中的人物跳跃和人物跑步,此时只需给该状态添加一个下蹲的状态即可
else if (heroDown) {//下蹲状态heroIndex++;if (heroIndex >= 2) {heroDown = false;heroIndex = 0;}}
我只贴出了下蹲部分的代码,当heroDown为true说明执行下蹲,就改图片帧,当帧数大于等于2的时候,说明一次下蹲操作结束了,此时就可以将下蹲标志设置为false,将图片帧也得归零
这个下蹲的速度非常快,不到1s就结束了,所以没有截图到,除了速度块基本上这个下蹲操作就已经实现了,现在来优化一下这个下蹲的速度
下蹲速度优化
为什么速度会这么快呢?
因为这个下蹲的图片只有两张,循环一次的时间也就几十毫秒,所以速度很快,解决这个问题有两种方法,第一个就是,将这个下蹲过程的图片设置很多张,例如下蹲的图片有50张,将其全部渲染出来,这就加长了时间,当然也是很费内存的,第二种就是用计数器,利用空循环当达到计数器设定的值的时候在进行执行下蹲,由于下蹲第二张渲染呈现时间比第一张的长,这也显得下蹲才逼真,解决这一问题,需要定义一个数组,存储两张图片的计数,以下为代码:
else if(heroDown){static int count = 0;int dalays[2] = { 4,10 };count++;if (count >= dalays[heroIndex]) {count = 0;heroIndex++;if (heroIndex >= 2) {heroIndex = 0;heroDown = false;}}}
利用static记录循环执行的次数,dalays保存两张图片的计数,heroIndex序列帧循环两个图片,如果满足的话,就将count设为0,执行下蹲操作
渲染下蹲操作
人物跳跃的渲染在main函数里面通过一行代码实现了,但是现在人物的状态有两种,跳跃与下蹲,此时就需要封装为函数了,创建updateHero函数
在main函数里用updateHero函数代替人物奔跑的代码
void updateHero() {//实现人物的奔跑if (!heroDown) {putimagePNG2(heroX, heroY, &imgHero[heroIndex]);}else {int y = 345 - imgDown[heroIndex].getheight();putimagePNG2(heroX, y, &imgDown[heroIndex]);} }
如果处于非下蹲操作,由于人物都是一个高度,当处于下蹲时候,两张图片的高度不一样,所以需要利用图片序列帧计算y的坐标
此时的下蹲代码算是写完了,下面时运行结果此时就很容易的截图到该英雄的下蹲操作了,接下来实现随机障碍物
看看上面几个小标题就是开发这个模块的流程
五.随机障碍物的实现
如何实现随机障碍物呢?
此时就会涉及到枚举的应用,障碍物的封装,障碍物的池子和随机数
枚举 : 可用提高代码的可读性,简化程序,一般一定个数的东西可定义为枚举类型,枚举里的元素,也就是整数类型
首先定义枚举类型,就将障碍物设置为乌龟,狮子,四种柱子
typedef enum {TORTOISE,//乌龟 0LION,//狮子 1HOOK1,//柱子 2HOO2,HOO3,HOOK4,OBSTACLE_TYPE_COUNT // 总数
}obstacle_type;
在这里用到的OBSTACLE_TYPE_COUNT 很是巧妙,枚举里的值从0开始,到了OBSTACLE_TYPE_COUNT 刚好时前面障碍物的总数,此时就将枚举定义好了,然后就可以封装结构体了
首先应该知道封装的属性都有什么,一个障碍物,他得有类型,坐标,速度,伤害,使用状态,此时我们可用再添加一个图片的帧序列,因为每个障碍物有的是动态的,都有序列,此时就可以将初始化加载图片进行优化,要用到一个大小可变的容器vector来存储,声明为二维的,每一维存储该组图片
vector的使用需要导入头文件vector #include<vector>
代码中 obstacleImgs为定义在全局的二维数组,在初始化时候,创建个一维数组,最后再将其一维数组添加到该二维数组里
vector<vector<IMAGE>>obstacleImgs;//存放所有障碍物的各个图片
此时所有障碍物的图片存在于二维数组obstacleImgs中了
封装结构体
typedef struct obstacle {int type;//类型,由于类型定义在枚举种,枚举里的变量就相当于整数类型,所以可用int代替int x, y;//坐标int imgIndex;//帧序列int speed;//速度int power;//伤害bool exist;//是否可用}obstacle_t;
创建障碍物池子
也就是定义一个结构体数组,OBSTACLE_COUNT是定义的宏,池子的大小
obstacle_t obstacles[OBSTACLE_COUNT];//障碍物池子
在封装了障碍物之后,那么之前小乌龟所定义的地方都需要优化了
小乌龟的定义
创建小乌龟
fly函数中小乌龟的运动
障碍物的渲染层
此时可用依据上面删除的部分进行开发,定义我们已经做了但是应该将池子里的exist属性进行初始化,以保证能够正确的知道哪个障碍物可用
void init(){...//初始化障碍物池子for (int i = 0; i < OBSTACLE_COUNT; i++) {obstacles[i].exist = false;} }
接下来需要创建小乌龟,此时应该重写creatObstacle函数,
开发思路 : 先用for循环在池子里面找到一个可用使用的障碍物,也就是exist为false的,然后再设定他的各属性
void creatObstacle() {int i = 0;//找到一个可以用的障碍物for (i = 0; i < OBSTACLE_COUNT; i++) {if (obstacles[i].exist == false) break;}obstacles[i].exist = true;obstacles[i].imgIndex = 0;obstacles[i].type = rand() % 6;//取0-5的随机数obstacles[i].x = WIN_WIDTH;obstacles[i].y = 345 + 5 - obstacleImgs[obstacles[i].type][0].getheight();if (obstacles[i].type == TORTOISE) {//小乌龟obstacles[i].power = 5;obstacles[i].speed = 0;}else if (obstacles[i].type == LION) {//狮子obstacles[i].power = 20;obstacles[i].speed = 5;}else if (obstacles[i].type >= HOOK1 && obstacles[i].type<= HOOK4) {//四个柱子obstacles[i].power = 20;obstacles[i].speed = 0;//静态的obstacles[i].y = 0;//由于柱子是在填上挂着,所以将其y设置为0} }
这个初始化看着比较多,但是难度不大,就找到一个可以用的障碍物,然后将其封装的属性进行初始化,实现随机就是再枚举里面取随机数,只有狮子是跑过来的,所以要和第三层草坪背景图的速度不能保持一致,其他障碍物的速度设置为0即可实现初始化
fly中更新障碍物的数据
更新x坐标使其运动,更新图片帧序列使其处于动态
void init(){...//更新各障碍物的状态 for (int i = 0; i < OBSTACLE_COUNT; i++) {if (obstacles[i].exist) {obstacles[i].x -= (obstacles[i].speed + bgSpeed[2]);if (obstacles[i].x < -obstacleImgs[obstacles[i].type][0].getwidth() * 2) {//已经从左边跑出了屏幕obstacles[i].exist = false;}//更新该障碍物的帧序列int len = obstacleImgs[obstacles[i].type].size();obstacles[i].imgIndex = (obstacles[i].imgIndex + 1) % len;}}}
代码解释 : 从障碍物池子里面找正字使用的障碍物,找到之后,再改变他的x坐标,bgSpeed[2]为草坪的速度,当减去他的时候,和草坪是相对速度为0,再减去该障碍物的速度,就是和草坪的相对速度,若不为0,此时就能显示出运动的状态,若为0,就和草坪相对静止
渲染障碍物
void updateEnemy() {for (int i = 0; i < OBSTACLE_COUNT; i++) {if (obstacles[i].exist) {putimagePNG2(obstacles[i].x, obstacles[i].y, WIN_WIDTH,&obstacleImgs[obstacles[i].type][obstacles[i].imgIndex]);}}}
渲染的图片的第一维是该图片的类型,第二维是该图片的帧数,很巧妙
这样就设计完了,看看成果
我跑了半分钟,感觉这个柱子出现的频率还是太大了,因为当初随机数是对6取余的,二柱子就占了四个,所以这里可用优化
此时类型的这里就化解了,取两次随机数,让其柱子出现的几率降低
此时就能看到这几个障碍物同框了,但是碰撞这里还没有做,现在随机障碍物也实现了
六.实现英雄与障碍物的碰撞检测
从图可以看出,障碍物的碰撞检测就是在检测两个矩形是否相交,这种判断矩形相交的代码在网上开源的有很多
如果以白边的坐标来检测的话,可能会有误差,则加上偏移量,使判断更加准确,
分析 : 碰撞检测实在数据层进行的,但是这个功能可封装为函数,所以在fly函数里面定义一个checkHit函数用于检测碰撞
一下是判断是否碰撞的代码,主要是找到这四个点的坐标,加上偏移量即可
开源代码,判断矩形是否相交
//设A[x01,y01,x02,y02] B[x11,y11,x12,y12]. bool rectIntersect(int x01, int y01, int x02, int y02,int x11, int y11, int x12, int y12) {int zx = abs(x01 + x02 - x11 - x12);int x = abs(x01 - x02) + abs(x11 - x12);int zy = abs(y01 + y02 - y11 - y12);int y = abs(y01 - y02) + abs(y11 - y12);return (zx <= x && zy <= y); }
找四个点的坐标,调用rectIntersect函数进行判断是否相交
void checkHit() { //实现碰撞检测for (int i = 0; i < OBSTACLE_COUNT; i++) {if (obstacles[i].exist) {int a1x, a1y,a2x,a2y;int off = 30;if (!heroDown) {//非下蹲状态a1x = heroX + off;a1y = heroY + off;a2x = heroX + imgHero[heroIndex].getwidth() - off;a2y = heroY + imgHero[heroIndex].getheight();}else {//下蹲状态a1x = heroX+off;a1y = 345 - imgDown[heroIndex].getheight();a2x = heroX + imgDown[heroIndex].getwidth()-off;a2y = 345;}IMAGE img = obstacleImgs[obstacles->type][obstacles->imgIndex];int b1x = obstacles[i].x + off;int b1y = obstacles[i].y + off;int b2x = obstacles[i].x + img.getwidth() - off;int b2y = obstacles[i].y + img.getheight() - 10;if (rectIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y)) {//相交heroBlood -= obstacles[i].power;printf("剩余血量 : %d\n", heroBlood);playSound("res/hit.mp3");}} }}
此时有个bug,碰撞一次,连续掉血多次
bug原因 : 一帧一帧的检测
解决方法 : 结构体中添加属性,是否碰撞,在进行对其初始化,最后在碰撞检测函数里面优化
1.添加了判断条件
2.当判断碰撞后,将其hited设置为true
效果图:
以上就是英雄与障碍物的碰撞检测模块了
七.总结
主要学习开发思想,一些开发技巧,将语法用到实战,了解计时器,计数器,枚举,结构体在开发中的应用,灵活运用函数封装提高程序的可读性,如何改善了用户点击休眠时的问题
相关文章:

[C/C++]天天酷跑超详细教程-中篇
个人主页:北海 🎐CSDN新晋作者 🎉欢迎 👍点赞✍评论⭐收藏✨收录专栏:C/C🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!ǹ…...

面试被打脸,数据结构底层都不知道么--回去等通知吧
数据结构之常见的8种数据结构: -数组Array -链表 Linked List -堆 heap -栈 stack -队列 Queue -树 Tree -散列表 Hash -图 Graph 数据结构-链表篇 Linklist定义: -是一种线性表,并不会按线性的顺序存储数据,即逻辑上相邻…...

微服务面试问题小结( 微服务、分布式、MQ、网关、zookeeper、nginx)
什么是微服务,单体架构的优点和缺点,微服务架构的优点和缺点? 单体架构 优点:架构简单,维护成本低缺点:各个模块耦合度太高,当对一个模块进行更新修改时,会影响到其他模块ÿ…...

Vue3全局变量使用
全局变量(函数等)可以在任意组件内访问,可以当组件间的传值使用。 main.js import ./assets/main.cssimport { createApp } from vue import App from ./App.vueconst app createApp(App); app.config.globalProperties.$global_id10; app.…...

拼多多海量商品数据接口API 商品详情接口 商品价格主图接口
拼多多,作为中国最大的社交电商之一,提供了丰富的商品信息和海量的用户数据。对于广大开发者而言,如何快速、准确地获取这些数据,进而开发出各种创新应用,是他们关心的问题。本文将详细介绍拼多多海量商品数据接口API的…...

结构化日志记录增强网络安全性
日志是一种宝贵的资产,在监视和分析应用程序或组织的 IT 基础结构的整体安全状况和性能方面发挥着至关重要的作用。它们提供系统事件、用户活动、网络流量和应用程序行为的详细记录,从而深入了解潜在威胁或未经授权的访问尝试。虽然组织历来依赖于传统的…...

企业架构LNMP学习笔记5
Nginx: 常见用法: 1)web服务器软件 httpd http协议 同类的web服务器软件:apache Nginx(俄罗斯)IIS(微软)lighttpd(德国) 2)代理服务器 反向代…...

Idea安装免注册版ChatGPT
文章目录 一、前期准备二、开始使用 一、前期准备 1.准备Idea开发软件并打开(VS Code同理)! 2.【CtrlAltS】快捷键调出Settings窗口,如图 3.找到NexChatGPT 此插件不需要注册,可以直接使用(高级一些的需要会员收费限…...

git操作
一、查看远程分支 使用如下git命令查看所有远程分支: git branch -r 查看远程和本地所有分支: git branch -a 查看本地分支: git branch 在输出结果中,前面带* 的是当前分支。 二、拉取远程分支并创建本地分支 方法一 使用如下…...

9 | 求出不同性别和不同科目的学生平均分数
需求描述:学生成绩分析 背景: 我们有一组学生的成绩数据,其中包括学生的姓名、性别和科目,我们需要分析不同性别和不同科目的学生平均分数。 功能要求: 从数据源中获取学生的成绩数据,包括学生姓名、性别和科目。使用Spark进行数据处理,将学生数据按性别和科目分组。计…...

Java如何发起http的get请求的实现
加哥最近做第三方接口开发,对方提供的是get方式的http请求,下面加哥给大家进行了总结如何用java代码去发送http请求并获取结果。 下面是发送get请求的工具类 1.不要求携带token的方式 public static String getUrl(String tempurl,String bm) {String…...

webRtc 示例
1、使用socket.io进行会话 2、为了方便,参数写死在前端了,前端界面1代码如下(由界面1发起视频): <!DOCTYPE html> <html><head><title>Socket.IO chat</title><meta charset"…...

【RabbitMQ】服务启动成功,无法访问localhost:15672(RabbitMQ Management)
问题描述 RabbitMQ 服务已经启动成功,已经安装rabbitmq_management插件,无法访问RabbitMQ Management(http://localhost:15672/)。 原因分析 15672端口被Microsoft Edge占用。 解决方案 打开cmd终端,输入指令&#…...

【操作记录】pytorch_geometric安装方法
pytorch_geometric安装方法 github地址 主要不要直接pip install安装,会由于依赖无法安装而失败 点击here手动安装依赖 选择对应的pytorch版本,我的是Win10 Python3.8.3Pytorch1.8.1CUDA10.2 手动下载四个依赖包本地安装: 主要不要直接&am…...

EventSystem 事件系统
EventSystem 事件系统 事件系统在开发中必不可少事件系统使用观察者模式可以极大程度降低程序的耦合,之前的文章也讲过事件系统但是不够高效简洁,如何轻便高效优雅的实现一个事件呢?依然基于之前的AssemblyManager 程序集管理器和SingletonS…...

2.2 Vector<T> 动态数组(模板语法)
C数据结构与算法 目录 本文前驱课程 1 C自学精简教程 目录(必读) 2 动态数组 Vector(难度1) 其中,2 是 1 中的一个作业。2 中详细讲解了动态数组实现的基本原理。 本文目标 1 学会写基本的C类模板语法; 2 为以后熟练使用 S…...

dockerfile 例子(二)
Dockerfile由一行一行的命令语句组成,#开头的为注释行。Dockerfile文件内容分为四个部分:基础镜像信息、维护者信息、镜像操作指令以及容器启动执行指令。 接下来给大家列出Dockerfile中主要命令的说明。 FROM,指定所创建镜像的基础镜像。 …...

openssh---Windows下git安装配置gitlab
安装openssh 1. 专业版Win10/11默认自带,可以查看是否开启 1. Get-WindowsCapability -Online | Where-Object Name -like OpenSSH* 2. Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0 3. Add-WindowsCapability -Online -Name OpenSSH.Serve…...

vscode宏键绑定
开发语言php 实现输入[ 得到 [];的效果 [win]ctrlp,[mac]superp 输入>keyboard 选择 在json文件里增加(目前有缺陷,sublime的设置是比较完美的.或者phpstorm默认不需要配置): {"key": "[","command": "editor.action.insertSnippet&…...

外贸企业如何借助CRM提升企业发展?
外贸企业竞争激烈,提高自身竞争力,扩大海外业务市场,是每个外贸企业的目标。为了实现这一目标,不少外贸企业借助CRM系统,优化业务流程,管理维护客户,从而实现可持续发展。那么,外贸企…...

初步了解ES
一、ES基础查询 1、es基础查询 1.1 准备数据 # 准备数据 PUT test_index/_doc/1 {"name":"顾老二","age":30,"from": "gu","desc": "皮肤黑、武器长、性格直","tags": ["黑", &…...

Linux基础(三)
一.系统基本优化 关闭selinux:getenforce 查看selinux状态setenforce 0 临时关闭vim /etc/sysconfig/selinux 永久关闭SELINUXdisabled 关闭防火墙:systemctl stop firewalld 临时关闭防火墙systemctl disable firewalld 永久关闭防火墙sys…...

python函数调用的四种方式
第一种:参数按顺序从第一个参数往后排#标准调用 def normal_invoke(x, y):print("--normal_invoke:--" )print("x is %d" %x )print("y is %d" %y) # 标准调用 normal_invoke(1, 2) 运行结果: --normal_invoke:-- x is 1 …...

如何将两个pdf合并成一个?pdf合并技巧分享
在日常工作过程当中,我们经常需要处理一些文件,而文件的处理往往是琐碎的,想要提高工作效率,需要选择一些合适的方法,并掌握一定的技巧,那么,如何将两个pdf合并成一个?pdf合并技巧有哪些呢?接…...

qt : day 3
1.完成登录框的按钮操作,并在登录成功后进行界面跳转 ------------------------------------------------------------------ .pro ------------------------------------------------------------------ QT core gui texttospeech greaterThan(QT_MAJOR_V…...

flutter高德地图大头针
1、效果图 2、pub get #地图定位 amap_flutter_map: ^3.0.0 amap_flutter_location: ^3.0.0 3、上代码 import dart:async; import dart:io;import package:amap_flutter_location/amap_flutter_location.dart; import package:amap_flutter_location/amap_location_option…...

【线性代数】矩阵求导的本质与分子布局、分母布局的本质(矩阵求导——本质篇)
矩阵求导的本质与分子布局、分母布局的本质(矩阵求导——本质篇) 说在前面一. 函数与标量、向量、矩阵二. 矩阵求导的本质三. 矩阵求导结果的布局四. 分子布局、分母布局的本质五. 向量变元的实值标量函数 说在前面 我将严谨地说明矩阵求导的本质与分子布…...

快速了解状态管理库Pinia及其使用方法
目录 1.pinia是什么 2.为什么要使用pinia 3.pinia的优点 4.pinia在项目中使用 ①创建一个使用pinia的Vue3项目 ②在页面使用store 1.pinia是什么 Pinia 起源于一次探索 Vuex 下一个迭代的实验,如果你学过Vue2,那么你一定使用过Vuex。Vuex在Vue2中主…...

scratch绘制同心圆 2023年5月中国电子学会图形化编程 少儿编程 scratch编程等级考试四级真题和答案解析
目录 scratch绘制同心圆 一、题目要求 1、准备工作 2、功能实现 二、案例分析 <...

【LeetCode】3. 无重复字符的最长子串
3. 无重复字符的最长子串(中等) 方法:滑动窗口 哈希表 思路 这道题主要用到思路是:滑动窗口 什么是滑动窗口? 其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 ab…...