单调栈单调队列【算法进阶】

这周学完之后最大的收获就是单调栈和单调队列了!!!感觉好厉害能把时间复杂度瞬间压缩为O(N),不行我必须再纪念一下这么美妙的算法!!!

单调栈问题:

如果题目要求一个元素左边或右边的第一个大于或小于他的元素(一把不会直接告诉你,需要你在分析问题时对是否有单调性或者是否需要单调性进行分析),或者让你求一段连续子数组的左右边界问题(如柱状图的最大矩形面积)时(力扣上问题84、85、或者洛谷上最大区间问题等等),可以优先考虑用单调栈,而如果要以当前元素为右端点,在他之前找到极值时,一般用滑动窗口,而且滑动窗口一般不会只考双端队列,而是用单调队列进行优化(队首/或者说越靠前一定是最优选择)例如力扣862.和至少为K的子数组,先用前缀和预处理,要想用最短的长度满足大于等于K 那么就要让窗口尽量小,且遍历时右端元素与队首元素的差最大,也就是让队首元素最小,固需要维护单调递增队列,用于找到目前以这个元素为右端点时前面的最小值!

进阶题目练习:

1、柱状图中的最大矩形

这个题目就是让求一段连续的区间,然后让区间长度与区间内的所有元素的最小值的乘积最大,首先用暴力的方法来想,那就是遍历每一个矩形,那么怎么优化呢,我们可以遍历每一个元素,然后以当前元素为最小值的连续矩形,这样我们就从遍历每一个矩形变成了遍历每一个可能作为答案的矩形(最多有n个矩形),那么怎么去实现找到以当前元素为最小值的矩形呢?开个循环往两边找边界?诶?往两边找?边界?那不就是分别找当前元素两边的第一个小于他的位置吗?恭喜你,想到了正解 -> 单调递增栈优化,通过正反两次单调递增栈找到以这个元素为最小值的矩形的左右边界即可。        【从暴力优化到正解!✌️】

class Solution {
public:int largestRectangleArea(vector<int>& heights) {int ans = 0,n = heights.size();vector<int> l(n),r(n);stack<int> st;for(int i=n-1;i>=0;i--){int t = heights[i];while(!st.empty() && heights[st.top()] >= t) st.pop();r[i] = (st.empty() ? n : st.top());st.push(i);}st = stack<int> ();for(int i=0;i<n;i++){int t = heights[i];while(!st.empty() && heights[st.top()] >= t) st.pop();l[i] = (st.empty() ? -1 : st.top());st.push(i);}for(int i=0;i<n;i++){ans = max(ans,(r[i] - l[i] - 1)*heights[i]);}return ans;}
};

至于最大区间,和上面的题一模一样,就当复习了,代码略~

什么?难度又要升级了?来看一下:最大矩形

还是一样,先想暴力,枚举每一个矩形,找出最大值,从上往下来看,这不是和前面的题目一样吗,从一个个柱状图中找出最大的连续矩形(可是有可能同一列的柱状图不连续啊)那就只能一行一行的找喽,那就对每一行进行上一题的步骤,不断更新最大值,总比纯暴力枚举每一个矩形强吧,时间复杂度是O(N*M),看一眼数据范围能过。

怎么实现呢?首先进行预处理,可以先把每一行的连续柱状图表示出来,然后对每一行进行两次单调栈查询以当前元素为最小值的矩阵的左右范围,不断更新即可!

class Solution {
public:int maximalRectangle(vector<vector<char>>& matrix) {int n = matrix.size();//n行int m = matrix[0].size();//m列vector<vector<int>> dp(n,vector<int>(m,0));//预处理值(这行中这一列的连续的1的个数即:高度)for(int j=0;j<m;j++)for(int i=0;i<n;i++)if(matrix[i][j] == '1')dp[i][j] = (i == 0) ? 1 : dp[i-1][j] + 1;int ans=0;for(int i=0;i<n;i++)//对每一行都仿照84题的解法{vector<int> l(m,0),r(m,0);//以当前元素为最小值的左右区间stack<int> st;for(int j=0;j<m;j++){int t = dp[i][j];while(!st.empty() && dp[i][st.top()] >= t) st.pop();l[j] = st.empty() ? -1 : st.top();st.push(j);}st = stack<int> ();for(int j = m-1;j>=0;j--){int t = dp[i][j];while(!st.empty() && dp[i][st.top()] >= t) st.pop();r[j] = st.empty() ? m : st.top();st.push(j);}for(int j=0;j<m;j++){int x = r[j] - l[j] - 1;int S = x*dp[i][j];ans = max(ans,S);}}return ans;}
};

太美妙了这单调栈!

滑动窗口&单调队列优化

双端队列滑动窗口(常规滑动窗口)

单调队列滑动窗口
                                               1.维护左端点

                                               2.维护右端点的队列单调性

                                               3.元素入队 

长度最小的子数组

由于每一个元素都大于0,所以前缀和本身就具有单调性,无需用到单调队列进行优化,直接二分前缀和即可,或者用双端队列/双指针模拟滑动窗口来实现。

class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int ans=1e8;deque<int> q;int sum=0;for(int i=0;i<nums.size();i++){q.push_back(i);//本身就具有单调性 所以可以直接进队sum+=nums[i];while(!q.empty() && sum>=target){ans = min(ans,q.back() - q.front() + 1);sum-=nums[q.front()];q.pop_front();}  }return ans==1e8 ? 0 : ans;}
};

当然更推荐符合大众的模板风格:维护左端,维护结束后再判断答案

这样写的好处是只要遍历到了一个合法答案 那么之后的所有遍历中答案都合法了,所以再更新答案时要加上if判断合法答案,防止一次while都没进过 就应该是没有合法答案了 所以while中是多余元素的判断!

class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int ans=1e8;deque<int> q;int sum=0;for(int i=0;i<nums.size();i++){q.push_back(i);//本身就具有单调性 所以可以直接进队sum+=nums[i];while(!q.empty() && sum - nums[q.front()]>=target){sum-=nums[q.front()];q.pop_front();} //if是必须要加的防止一次while都没进入过!!!if(sum >= target) ans = min(ans,q.back() - q.front() + 1);}return ans==1e8 ? 0 : ans;}
};

和至少为K的最短子数组

这道题让求满足一定条件的子数组,而且还要求出最短的长度,首先想暴力怎么实现,遍历每一个子数组,再计算区间和(前缀和优化),然后再找出最小的满足条件的子数组,显然时间复杂度是通过不了此题的,那么就开始想怎么去优化一下,首先毋庸置疑要先用前缀和进行预处理,然后再找满足条件的子区间 -> 要想使区间和大于等于K,那么就要在遍历数组时,让区间的左端点尽量的小,因为这样才能保证区间尽可能的大(首先要保证存在性,然后再取最优解),而且还要保证区间的左端点一定要比右端点小,也就是说在遍历时,如果当前元素是目前为止出现的最小值了,那么就应该将这个元素为起点继续往后找了因为越小的值越作为起点,继续往后遍历时肯定是要找他之前的最小值),这样也就符合单调队列的性质了,所以单调队列优化滑动窗口可以适用于求一个元素前面的最值问题,基本步骤是先维护左端点(并计算答案【有存在解时】)再通过右端点维护队列的单调性,然后最后再从队尾push_back新元素。所以就有一下代码:

class Solution {
public:int shortestSubarray(vector<int>& nums, int k) {int n = nums.size();vector<long long> sum(n+1,0);//前缀和预处理for(int i=0;i<nums.size();i++) sum[i+1] = sum[i] + nums[i];int ans = 1e8;deque<int> q;q.push_back(0);//因为是前缀和 所以要多往前压一个下标for(int i=1;i<=n;i++){int t = sum[i];//在满足条件情况下更新ans并将队首弹出while(!q.empty() && sum[i] - sum[q.front()] >= k){//因为往前移了一个元素 所以无需-1ans = min(ans,i-q.front());q.pop_front();}//维护单调递增队列while(!q.empty() && sum[q.back()] >= t) q.pop_back();q.push_back(i);}return ans==1e8 ? -1 : ans;}
};

最小覆盖子串 

附:先加入新元素和后加入新元素的判断:

先加入新元素和后加入区别:

先加入是因为while中是合法解,要在while中找答案,while之后可能不合法

后加入是因为while中是不合法解,while之前可能还是不合法,while之后才是合法解

                                                                                                        【在合法解中找答案!】 

总之 主要就是看什么时候加入元素能可能出现合法解 。

1-先加入新元素合法 就先加入新元素

2-先加入新元素有可能不合法 就先解决矛盾 后加入

代码如下:

class Solution {
public:string minWindow(string s, string t) {int min_len = s.size() + 1;//满足条件的解的长度int l = -1;//满足条件的解的起始位置unordered_map<char,int> v;//记录t中需要的字符 不能用bool因为可重复unordered_map<char,int> mp;//用于记录当前的窗口中的所有字符出现的情况for(int i=0;i<t.size();i++) v[t[i]] ++;//预处理vdeque<int> q;//滑动窗口int num=0;//当前窗口中的t中所需字符的数量for(int i=0;i<s.size();i++){q.push_back(i);//先加入 因为加入新元素后有可能满足条件if(v[s[i]] && mp[s[i]] < v[s[i]]) num++;//累加nummp[s[i]]++;//考虑到所有元素 因为要求最小值 不是t中的元素就可以出去 从而找到最小值while(!q.empty() && mp[s[q.front()]] > v[s[q.front()]]){//没有用的(多余的t中的元素的或t中没有出现的)元素就扔掉mp[s[q.front()]]--;q.pop_front();}if(num == t.size() && q.back() - q.front() + 1 < min_len){//只要有满足条件的解了就更新min_len = q.back() - q.front() + 1;l = q.front();}}return l > -1 ? s.substr(l,min_len) : "";}
};

后加入的例题:最长优雅子数组详解请看前几天的这个博客 。

代码如下:

class Solution {
public:int longestNiceSubarray(vector<int>& nums) {int ans=0,num=0;//num是当前窗口中的或运算deque<int> q;for(int i=0;i<nums.size();i++){while(!q.empty() && num & nums[i])//有交集 用按位或运算优化代码{num ^= nums[q.front()];//维护左端点 q.pop_front();}num |= nums[i];//或运算的优点:二进制位上只要有一个为1就为1(说明有人占位了 不能再占了 后续判断待判元素时就不需要一个一个按位与了 只需要对num来按位与即可 因为此时的num就已经是窗口中的所有元素按位或的结果了)q.push_back(i);//维护右端点 将待判元素入队//解决完矛盾之后就一定是合法解了 直接更新即可ans = max(ans,q.back() - q.front() + 1);}return ans;}
};

总结:

单调栈总结:

单调栈可以求出一个元素的一边的第一个大于或小于他位置,所以可以适用于求连续子数组的边界问题如力扣-接雨水、力扣-柱状图中的最大矩形、力扣-最大矩形等,再有就是一些比较明显的求一边的第一个大于或小于他的元素的位置了,就不过多赘述了。

滑动窗口总结:

滑动窗口分为两种:

1.双端队列实现:如果新元素与之前的元素没有冲突或影响,一般可以先入队,再维护左端点,但是如果新元素与之前的元素有了冲突(最长优雅子数组),就需要先维护左端点,直到没有冲突时再加入新元素,这样就能保证每次遍历中的队列都是一个满足题目要求的队列了,所以每次都更新最优解即可。

总之都是要先保证有解,再取最优解。 

2.单调队列优化:

一般的步骤就是先维护左端点,再通过维护右端点来维护队列的单调性,然后再加入新元素(一般用于找一个元素之前的最值)。

双端队列实现:队列这个窗口就是维护的满足条件的子数组区间,一般见于最短最长连续子数组

单调队列实现:队列中的是这个元素之前的单调小于或大于这个元素的值,而且队首就是最值。

/*
----------------=-=============================+++++++++++++++++++++++++++++**+*********************
-------------------==-=-=======================++=+++++++++++++++++++++++++++++*********************
:----------------------==========================+++++++++++++++++++++++++++++**+*******************
::-----------------------===-=-==========================+++++++++++++++++++++++++*+****************
:::::------------------------=====---::::...............:::::-==++++++++++++++++++++++*++***+*******
::::::-:-------------------==--::...............................::-=++++++++++++++++++*++++*++++++++
::::::::-:----------------:..........................................:-=++++++++++++++++++*+++++++++
::::::::::::-----------:.   ............................................:=+++++++++++++++++*+*******
:::::::::::::::------.    .............................::.................:-+++++++++++++++++++*****
::::::::::::::::-=:.   ...............................::...::::--:...........-+++++++++++++++++****+
:::::::::::::::--: .................................:-:..::..  .+*-:...........-++++++++++++++++++++
:::::::::::::-=:........:...........................-:..-.      +@#--:..........:+++++++++++++++++++
::::::::::::-=:.........::.............................==     .+%%%#.-:.........:-++++++++++++++++++
...::::::::--.....:--:::.::...........................:%%=::-*%%%%%%+ -:.......:---=++++++++++++++++
..::::::::--.....:+*. .::.::..........................+%%%%%%%%%%%%%%. -......:-----=+++++++++++++++
.......:.:=....:-=%=    ::............................*%%%%%%%%%%%%%%= :-.....:------=*+++++++++++++
.........=:....-:#%#.   =+...........:...............:#%%%%%%%%%%%#%%* .-.....:-------=*++++++++++++
........:=....:.=%%%#+=+%#:...........................#%#%==%%%%%%#%%*  -:....:--------+++++++++++++
........:-...:- +%%%%%%%%%:...........................+%*: -##%%*-*#%+  -:....:---------++++++++++++
........--...:: #%%%%%%%%%:...........................-#= .*:.###::#%-  -:....:---------++++++++++++
........-:...-. *%%#%%%%%%:............................+*=*#**####*#*...-:.....:--------=+=+++++++++
........-:...-. +%*.+@%#*#:.............................:------=====-::::......:---------*=+++++++++
........-:...-. :#-.+-##*#:.....................................................---------+===+++++++
........=:...::  +++*+#**=........::............................................---------+====++++++
........=:...:-..:*+=-::..........::..............::::::::::::::::::--==:.......:--------+====++++++
........-.....::::........::............:::--===+++++++++++++++*++++++**-.......---------+=====+++++
.......:-.........................::-==++++***+***+++++++++++++++++++++*+......:--------=*=======+++
.......-:............:::::::---==+++**+***++++++*++++++++++++++++++++++++:.....:--------=+=======+++
......:-.....::::-=+**########**+++++++*+++++++======--------------=+++++:.....:--------+=========++
......-:.....::-*##%%%#####%#*++++++++===----------------------------++++......--------++==========+
......:-........-#%########+====-------------------------------------=+*=.....:-------=+===========+
.......-:........:*%######*------------------------------------------+*+:....:-------==.:-+=========
.......:-.........:*%#####-------------------------------------------+*:.....:------=-:::.:++=======
........:-:........:+%##%*------------------------------------------++-.....:------=-..:::.-**++====
.........:-:.........=##%+-----------------------------------------++:....:------==---------++++*+++
...........--:........:*%#----------------------------------------+=:....:------==:::::::::...::::::
............:-::........-*+-------------------------------------==:....:------=-:...................
..............:--:.......:-=----------------------------------=--...::-----==-:.....................
................:--::.......:---------------------------------:...::-------=:..:....................
...................:--:.......::--------------------------::...::----=--:.-:........................
.....................:-=-:::.....:::----------------:::.....::--=---::...:-.........................
.................::::---..::::::::.......:::::.:......:::------::........-:.........................
.............:::::::-:.    ....::::::::::::::::::::------:::.............-:.........................
.........::::::...:-:      .............:::::::::........................=:.........................
......:::::......-:.  .    ..   .........................................=--::......................
...:--::........-:   ...       .   ......................................-=---::....................
:-:::.........:-:        ..   ..         ...........::::::::::::::::::::::+------:::................
::...........:-. .   . .        ........::.::::..::.:.................... :=---------::.............
............:-.        .......::........-......                       .    :=------------::::.......
...........:-. ......::::......        ::      ..           ..         . .  :=-----------------:::::
...........=:.:........      ..       .-.   ..          ..      .  .    .    .=---------------------
..........--...                ..     .- .        .                 .   .   . .:=-------------------
.........-=        .         . . .    :: ..     .    .   ..          .    ...   .-=-----------------
........:=.     .         .           -.                          .        ....  .::-=--------------
........-: .   ..  .   .         ..   -.                   ..    ... .      ..   ....::---=---------
*/

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

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

相关文章

C++编程基础

编程题一问题分析 题目要求使用 n 根小木棒&#xff0c;按照特定的方式排列&#xff0c;形成一个数字。具体规则如下&#xff1a; 每个数字由小木棒组成&#xff0c;例如&#xff1a; 1 需要 2 根小木棒。0 需要 6 根小木棒。其他数字&#xff08;如 2, 3, 4, 5, 6, 7, 8, 9&am…

张量拼接操作

一.前言本章节来介绍一下张量拼接的操作&#xff0c;掌握torch.cat torch.stack使⽤&#xff0c;张量的拼接操作在神经⽹络搭建过程中是⾮常常⽤的⽅法&#xff0c;例如: 在后⾯将要学习到的残差⽹络、注意⼒机 制中都使⽤到了张量拼接。二.torch.cat 函数的使用torch.cat 函数…

Dify 连接本地 SpringAI MCP Server

Dify 连接本地 SpringAI MCP server 连接 MCP server 的方式大致有两种&#xff0c;一种是基于 stdio&#xff0c;一种是基于 sse&#xff0c;如果对于稳定和性能好的方案的话&#xff0c;sse 要比 stdio 好的多&#xff0c;所以本文采用的是基于 sse 和 Spring AI 部署本地 MC…

基于 Python 的数据分析技术综述

先说一点个人的看法“”MDX、OLAP&#xff08;Mondrian&#xff09;技术更适合构建面向业务用户的标准化分析产品&#xff0c;尤其当产品需要满足以下特点时&#xff1a;分析维度固定&#xff08;如时间、区域、产品类别&#xff09;&#xff1b;需支持高并发查询&#xff08;如…

Live555-RTSP服务器

RTSP Server创建 RTSP服务器初始化&#xff1a; RTSPServer::createNew->new RTSPServer::RTSPServer->GenericMediaServer::GenericMediaServer->turnOnBackgroundReadHandling(IPV4sock/IPV6sock,incomingConnectionHandlerIPv4)如上流程&#xff0c;创建RTSP服务器…

Redis Stack扩展功能

Redis JSONRedisJSON是Redis的一个扩展模块&#xff0c;它提供了对JSON数据的原生支持。常用操作&#xff1a;-- 设置一个JSON数据JSON.SET user $ {"name":"loulan","age":18}## key是user&#xff0c;value就是一个JSON数据。其中$表示JSON数据…

Takebishi旗下智能硬件网关产品devicegateway详细介绍

一、产品概述 DeviceGateway是由日本Takebishi公司研发的一款专业工业物联网&#xff08;IIoT&#xff09;硬件网关产品&#xff0c;专为实现现场工业设备与云端平台、IT系统之间的高效、安全数据传输而设计。作为一款可靠的硬件网关&#xff0c;DeviceGateway具有即插即用、稳…

单向链表反转 如何实现

单向链表反转的实现方法 ​ https://www.zhihu.com/question/441865393/answer/3208578798 ​ 单向链表反转是数据结构中的经典问题&#xff0c;在面试和实际开发中经常遇到。以下是 多种实现方式&#xff08;包括递归和迭代&#xff09;&#xff0c;以 Go 语言为例。1. 单向链…

php+vue+Laravel音乐媒体播放及周边产品运营平台-nodejs-计算机毕业设计

目录具体实现截图课程项目技术路线开发技术介绍设计思路流程PHP核心代码部分展示详细视频演示/源码获取##项目介绍网络技术的广泛应用显著地推动了生活服务的信息化进程。结合音乐流媒体与周边产品的运营需求&#xff0c;构建一套音乐媒体播放及周边产品运营平台&#xff0c;成…

Python爬虫实战:研究xlwt 和 xlrd 库相关技术

1. 引言 1.1 研究背景与意义 随着电子商务的快速发展,电商平台积累了海量的商品数据。如何从这些数据中提取有价值的信息,为商家提供决策支持,成为电商领域的重要研究方向。传统人工采集和分析数据的方式效率低下,且容易出现错误。自动化数据采集与分析系统能够通过爬虫技…

【QGC】深入解析 QGC 配置管理

引言 在软件开发中&#xff0c;配置管理是一项至关重要的任务&#xff0c;它能帮助我们灵活地管理应用程序的各种参数和设置。QGroundControl&#xff08;QGC&#xff09;作为一款强大的开源无人机地面站软件&#xff0c;其配置管理系统设计精巧&#xff0c;值得我们深入学习。…

ChatGPT,从规则到强化学习

要了解 ChatGPT&#xff08;Chat Generative Pre-training Transformer&#xff09;&#xff0c;我们不得不先看看 NLP 自然语言处理&#xff08;Natural Language Processing&#xff09;。因为 ChatGPT 属于 NLP 领域&#xff0c;而 NLP 则又是人工智能的一个分支。 那么什么…

【目标检测之Ultralytics预测框颜色修改】

在 Ultralytics YOLOv8 中修改预测框颜色为红色&#xff0c;以下是三种实用方案&#xff1a;方案 1&#xff1a;直接修改 plot() 方法的 colors 参数 在调用 results.plot() 时直接指定颜色参数&#xff1a; from ultralytics import YOLO# 加载模型 model YOLO("yolov8n…

让 VSCode 调试器像 PyCharm 一样显示 Tensor Shape、变量形状、变量长度、维度信息

文章目录&#x1f3af; 目标&#xff1a;在 VS Code 调试器中自动显示这些变量信息&#x1f50d; 原理简介⚠️ 其他方案的局限性❌ 方案一&#xff1a;重写 __repr__❌ 方案二&#xff1a;向 debugpy 注册自定义变量显示器&#xff08;StrPresentationProvider&#xff09;✅ …

pip国内镜像源一览

以下是2025年主流pip国内镜像源完整清单及配置指南&#xff0c;综合多个权威来源整理的最新数据&#xff1a;一、核心镜像源推荐&#xff08;2025年稳定可用&#xff09;‌阿里云镜像‌https://mirrors.aliyun.com/pypi/simple/优势&#xff1a;依托阿里云CDN&#xff0c;全国平…

当大模型遇见毫米波:用Wi-Fi信号做“透视”的室内语义SLAM实践——从CSI到神经辐射场的端到端开源方案

作者 | Blossom.118 2025-07-12 关键词&#xff1a;CSI-SLAM、神经辐射场、毫米波、Transformer、数字孪生、开源 ---- 1. 为什么要“无摄像头”语义SLAM&#xff1f; • 隐私红线&#xff1a;欧盟GDPR 2024修订版把“摄像头点云”列入高风险生物特征&#xff0c;落地成本高。…

脉冲神经网络膜电位泄漏系数学习:开启时空动态特征提取的新篇章

脉冲神经网络膜电位泄漏系数学习&#xff1a;开启时空动态特征提取的新篇章 摘要 脉冲神经网络&#xff08;Spiking Neural Networks, SNNs&#xff09;作为第三代神经网络模型&#xff0c;凭借其事件驱动、高生物逼真度和潜在的超低功耗特性&#xff0c;已成为类脑计算与高效人…

SSRF(ctfshow)

web351-358这部分的题目都是明文的&#xff0c;按照题目要求绕过就行了<?php error_reporting(0); highlight_file(__FILE__); $url$_POST[url]; $xparse_url($url); if($x[scheme]http||$x[scheme]https){ if(!preg_match(/localhost|127\.0\.|\。/i, $url)){ $chcurl_ini…

亚矩阵云手机:重构物流供应链,让跨境包裹“飞”得更快更准

在跨境电商“时效即生命”的竞争中&#xff0c;物流信息滞后、清关效率低下、成本居高不下已成为商家最头疼的“三座大山”。传统模式下&#xff0c;人工更新物流状态耗时易错&#xff0c;跨境包裹常因清关延误遭客户投诉&#xff0c;而高昂的物流成本更直接吞噬利润。亚矩阵云…

HTML(5) 代码规范

HTML(5) 代码规范 引言 HTML(HyperText Markup Language)是一种用于创建网页的标准标记语言。HTML5 作为最新的 HTML 标准,自 2014 年正式发布以来,已经成为了构建现代网页应用的基础。本文将详细介绍 HTML5 代码规范,包括结构、语法、属性以及最佳实践等内容,旨在帮助…