[Lc滑动窗口_1] 长度最小的数组 | 无重复字符的最长子串 | 最大连续1的个数 III | 将 x 减到 0 的最小操作数
目录
1. 长度最小的字数组
题解
代码
⭕2.无重复字符的最长子串
题解
代码
3.最大连续1的个数 III
题解
代码
4.将 x 减到 0 的最小操作数
题解
代码
1. 长度最小的字数组
题目链接:209.长度最小的字数组
题目分析:
给定一个含有 n
个 正整数 的数组和一个正整数 target
。
找出该数组中满足其总和大于等于 target
的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
(子数组:是连续的!!!)
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
题解
- 注意题目说的是 正整数 数组,说明数组里面的数是大于等于0的数。
- 因此这道题我们有一种优化的方法。
- 题目让找连续的子数组和>=target,并且长度最小。有很多种情况,但是我们选择的是 最小长度。
算法原理:
不管什么题,首先我们一定会先想到的是 暴力求解,因为只有暴力求解出来了,我们就可以在暴力求解的基础上进行优化~
解法一:暴力枚举出所有的子数组的和
两层for循环,O(N^2)
for(int i=0;i<n;i++)for(int j=i;j<n;j++)sum+=num[j];
//固定一个数,挨个往后加if(sum>=target)min(len,j-i+1)
注意到此时暴力枚举是有优化的。
题目说的是一个 正整数数组,都是大于等于0的数,这个 sum是呈现出递增的状态的,单调递增!
在暴力求解中,此时right还要++,但是注意题目本来要求的就是 最小长度
此时sum在加上往上走了一步的right的num[right],一定是满足sum>=target,但是len变成5了,一定不会是最终结果
因此当条件已经满足sum>=target ,right就不用动了。right后面也就不用再枚举了。
那现在让 left+1,right和left指向同一下标,然后再重复上面过程,那有个问题,这段区间的和能不能直接算出来?
- 当然可以。
- 现在sum=8,我只需要把让sum减去num[left],不就是现在left和right所在的区间和算出来吗。
- 没有必要让right傻傻的回退然后重新加。因此right不动,更新sum=6.
因此我们从暴力枚举中发现两个优化:
- 一个是right 满足后,后面不用枚举
- 一个left++,right不用回退,
所以我们可以利用单调性,使用双指针优化。
解法二:利用单调性,使用 “同向双指针” 来优化
当我们在暴力枚举的策略中发现left和right都是从左向右一个方向移动,我们就称为这两个指针叫做同向指针。同向双指针又称为滑动窗口。
什么是滑动窗口?
本质上是 “同向双指针”,left从左到右移动,right不回退,从左到右移动,用left和right一直 维护这个区间的和,然后这两个指针从左向右移动的过程非常像一个窗口在这个数组里滑来滑去。
什么时候用滑动窗口?
利用单调性,用滑动窗口解决问题。
当我们发现在暴力求解时,两个指针都可以做到 不回退,都是向同一个方向移动的时候,此时就可以用滑动窗口。
滑动窗口怎么用?
- 初始left=0,right=0,充当窗口左端点,右端点。用left,right标记窗口左区间,右区间。
- 右窗口(++right)(右值进窗口)
- 判断
-
- 根据判断决定是否 左窗口(++left)(左值出窗口)
- 更新结果
-
- 2,3都有可能会更新结果,看题目要求
左窗口,判断,右窗口一直循环,直到right 超过区间长度结束,更新结果看题目要求(右窗口,左窗口都有可能),。
滑动窗口正确性
- 暴力枚举肯定对的,因为已经把所有子数组的情况都找出来了。
- 虽然滑动窗口并没有把没有把所有情况都枚举出来,但是这里利用单调性,规避了没有必要的枚举
- 虽然没有把所有情况真正枚举出来,但是已经判断出有些子数组不是最终结果,已经把所有结果都考虑进来了,所以这种策略是跟暴力枚举是一样正确的。
滑动窗口时间复杂度
进窗口是一个循环,判断也是一个循环。
两层循环套在一起。可能会觉得时间复杂度O(N^2),但是不能看代码算时间复杂度,要看实际情况分析实际复杂度。
实际我们只会让right向前移动,left也向前移动,即使时最坏情况,right移动到最后一个元素,left 也移动到最后一个元素,因为单调性,总共也就操作了 n+n=2n 次 整体时间复杂度O(N)。
代码
要考虑到,栈溢出(heap-buffer-overflow) 的边界情况
可详见前文
在Leetcode中,无穷大和无穷小分别怎么表示
C/C++中可以使用INT_MAX和INT_MIN
⭕2.无重复字符的最长子串
题目链接:3. 无重复字符的最长子串
题目分析:
给定一个字符串 s
,请你找出其中不含有重复字符的 最长 子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
子串和子数组都是连续的
❗❗❗ 模拟分析示例:
题解
算法原理:
首先还是暴力枚举,然后根据暴力枚举进行优化。
- 以下面为例,两层for循环,但是下面找到的结果都是我们站在上帝角度,编译器并不知到什么时候结束。
- 一般对应判断是否有重复元素,我们都可以用哈希表来解决问题。
- 使用哈希表,判断是否有重复元素,比如让你判断一个数组是否有重复,或者两个数组是否有重复都可以用哈希映射!
解法一:暴力枚举+哈希表(判断字符是否重复出现)
O(N^2)
根据解法一做优化,定义一个left,right指针。当right走到有重复的元素后,已经找到一个字串,其中left到right区间每个元素都已经进入hash表。
此时left向前走一步,但是这个区间还是有重复元素,因此left要走到没有重复的区间才行,
然后这个时候以前做法是right回退然后重新往下走,但是这里left到right区间元素本来就在hash表里
因此就不需要right回退了,而是向right继续向前走。然后重复上面过程,直到right走到结尾。结束~
这不就是滑动窗口的思想吗。双向指针,left往前走,right不回退一直往前走
解法二:利用规律,使用 “滑动窗口” 解决问题
- left=0,right=0
- 进窗口
- 判断
-
- 出窗口
- 更新结果
进窗口、判断、出窗口,更新结果是一个大循环过程。直到right到结尾循环结束。
其中判断、出窗口是一个小循环(直到跳出重复字符)。不过时间复杂度还是O(N).
注意:
- 更新结果可能在 进窗口后,判断后,出窗口后,判断后任意一个地方,看题目要求
本题:
- 进窗口 ->-> 让字符进入哈希表
- 判断-> 窗口内出现重复元素
- 出窗口-> 从哈希表中删除该字符
代码
class Solution {
public:int lengthOfLongestSubstring(string s) {//!!if(s.empty()) return 0; // 处理空输入vector<char> str;for(char c:s) str.push_back(c);int left=0,right=0,n=str.size(),len=0;//unordered_set ret;unordered_set<char> ret;while(right<n){
//先检查while(ret.count(str[right])){ret.erase(str[left]);left++;//利用了连续性//表中 发现了右元素已存在//要在左边 进行跳过}
//不存在 就插入ret.insert(str[right]);len=max(len,right-left+1);right++;}return len;}
};
总结一下:
利用单调性,使用 双指针 解决问题。
- 一般left和right,一个指向数组最左边,一个指向数组最右边,然后一次可以排除一批,再然后left++,–right,两个指针是对撞的。
这里利用单调性或者利用规律,使用 滑动窗口 解决问题
- 滑动窗口也使用双指针解决问题,不过left一直从前往后走,right不回退从前往后走,两个指针是同向的。因此滑动窗口本质其实是 同向双指针。
3.最大连续1的个数 III
题目链接:1004. 最大连续1的个数 III
题目分析:
给定一个二进制数组 nums
和一个整数 k
,假设最多可以翻转 k
个 0
,则返回执行操作后 数组中连续 1
的最大个数 。
示例 1:
输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:
输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
题解
题目说的翻转实际上是把0变成1的意思,最多翻转K次,说明小于等于K都是可以的。
拿到题我们开始肯定想的是暴力求解。
如果直接暴力求解,遇到0->1了,那下一次在遍历就有问题了。
因此我们换一个思路。这道题不是让转化后最大连续1的个数吗。
我们转化为:找出最长的子数组,数组里0的个数不超过K个,这个数组里面0一定能够转化成1。
算法原理:
解法一:暴力枚举+zero计数器
伪代码,两层for循环,统计zero的个数,满足zero>k,统计此时数组长度,然后重新进入循环,注意每次zero都清0
for(int i=0;i<n;++i)for(int j=i;j<n;++j)//双指针 查找出一段区间if(nums[j]==0)++zero;if(zero>k)ret=max(ret,right-left+1)
然后我们根据暴力枚举,看看有没有优化的可能。
定义两个指针left,right,right走到zero>k的位置,zero=3,大于k。
按照暴力求解left++,然后right回溯然后重新往后走。但是我们发现没有必要,现在left往前走一步,你会发现,right还是停留在老位置,这个区间不用在管的,直接丢弃。
因此,让left一直走到zero<=k的位置。然后 right也根本不用回溯 然后在重新走,而是直接往后走就行了。
根据上面的发现,当在暴力枚举中,发现left,right是同向移动的,利用这个规律,使用滑动窗口解决问题
解法二:利用规律,使用滑动窗口
- left=0,right=0
- 进窗口
- 判断
-
- 出窗口
- 更新结果
进窗口 -> 如果是1,不理会。如果是0,计数器+1
判断 -> zero>k
出窗口 -> 如果是1,不理会。如果是0,计数器-1
更新结果:在判断之后在更新
代码
class Solution {
public:int longestOnes(vector<int>& nums, int k) {int left = 0, right = 0, zero = 0;int n = nums.size(), len = 0;while (right < n) {//进 窗口if (nums[right] == 0)zero++;while (zero > k) {//循环判断if (nums[left] == 0)zero--;left++;}len = max(len, right - left + 1);right++;}return len;}
};
4.将 x 减到 0 的最小操作数
题目链接:1658. 将 x 减到 0 的最小操作数
题目分析:
给你一个整数数组 nums
和一个整数 x
。每一次操作时,你应当移除数组 nums
最左边或最右边的元素,然后从 x
中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。
如果可以将 x
恰好 减到 0
,返回 最小操作数 ;否则,返回 -1
。
示例 1:
输入:nums = [1,1,4,2,3], x = 5
输出:2
解释:最佳解决方案是移除后两个元素,将 x 减到 0 。
示例 2:
输入:nums = [5,6,7,8,9], x = 4
输出:-1
示例 3:
输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。
题解
这道题让每次从数组左右两边移除一个数,然后就是一个新的数组,然后再从新的数组再从左右两边移除一个数。
- 但是如果真的硬着头皮开始做,其实是很困难的。
- 并不知道每次是从最左边走还是最右边找。有可能这次左边下次右边或者还是左边,情况太复杂了。
因此我们可以利用 正难则反 的思想
- 正对面解题太难,那就想对立面,换个思路。
- 不是每次从左右两端找一个数吗,那可能找到情况就是a+b=x,a、b什么情况都要,但是中间这个连续区间的和不也是确定的吗sum-x
- 也就是这道题我们转换成,找出最长的子数组长度,所有元素的和正好等于sum-x,然后数组总长减去这段子区间长度不就是问题答案吗
- 如果没找到说明这个数组不存在将x减到0的数,直接返回-1
解法一:暴力求解
初始left,right指向同一下标,当right走到和大于target的时候,left往前走
按照暴力求解,right要回到和left相同下标,然后right在重新往前走,直到再次走到和大于target的地方停下来,然后重复上面过程。
- 但是今天这里不需要right回溯,因为right回溯后重新走到下面的位置,因为left已经往前走了,这段区间的和肯定是更小了
- 因此就不需要right回溯了。要么right不动,要么right往后走。
- 同向双指针 ----> 本质就是滑动窗口
解法二:使用滑动窗口
代码
class Solution {
public:
int minOperations(vector<int>& nums, int x)
{if(nums.empty()) return -1;int sum=0;for(auto c:nums)sum+=c;// 新增边界条件处理if (sum == x) return nums.size(); // 整个数组和正好等于xif (sum < x) return -1; // 总和不足,无法达成目标int target=sum-x,len=0;int left=0,right=0,n=nums.size(),add=0;while(right<n){add+=nums[right];while(add>target){add-=nums[left];left++;}if(add==target)len=max(len,right-left+1);right++;}//!!!len>0 return (len > 0) ? (nums.size() - len) : -1;
}
};
测试样例跑不全时,要注意对 边界情况 的处理
- 若不存在这样的子数组(
len = 0
)
相关文章:

[Lc滑动窗口_1] 长度最小的数组 | 无重复字符的最长子串 | 最大连续1的个数 III | 将 x 减到 0 的最小操作数
目录 1. 长度最小的字数组 题解 代码 ⭕2.无重复字符的最长子串 题解 代码 3.最大连续1的个数 III 题解 代码 4.将 x 减到 0 的最小操作数 题解 代码 1. 长度最小的字数组 题目链接:209.长度最小的字数组 题目分析: 给定一个含有 n 个 正整数 的数组…...

基于python的网络爬虫爬取天气数据及可视化分析(Matplotlib、sk-learn等,包括ppt,视频)
基于Python爬取天气数据信息与可视化分析(文末完整源码) 基于python的网络爬虫爬取天气数据及可视化分析 可以看看演示视频。 摘要 基于Python爬取天气数据信息与可视化分析 本论文旨在利用Python编程语言实现天气数据信息的爬取和可视化分析。天气…...
【缓存】缓存雪崩与缓存穿透:高并发系统的隐形杀手
缓存雪崩与缓存穿透:高并发系统的隐形杀手 在高并发系统中,缓存是提升性能的重要手段。然而,缓存使用不当也会带来一系列问题,其中最常见的就是缓存雪崩和缓存穿透。这两个问题如果不加以解决,可能会导致系统崩溃&…...
HTML AI 编程助手
HTML AI 编程助手 引言 随着人工智能技术的飞速发展,编程领域也迎来了新的变革。HTML,作为网页制作的基础语言,与AI技术的结合,为开发者带来了前所未有的便利。本文将探讨HTML AI编程助手的功能、应用场景以及如何利用它提高编程…...

李宏毅机器学习课程学习笔记04 | 浅谈机器学习-宝可梦、数码宝贝分类器
文章目录 案例:宝可梦、数码宝贝分类器第一步:需要定义一个含有未知数的function第二步:loss of a function如何Sample Training Examples > 如何抽样可以得到一个较好的结果如何权衡模型的复杂程度 Tradeoff of Model Complexity todo 这…...

AIGC(生成式AI)试用 26 -- 跟着清华教程学习 - DeepSeek与AI幻觉
目标:继续学习 个人理解: - AI幻觉:一本正经的胡说八道,你还觉得很道理,倾向于相信;事实不一致,指令(预期)与实际不一致:跑题 - 潜在风险:把AI带坏了;信息误…...
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_conf_add_dump
ngx_conf_add_dump 定义在src\core\ngx_conf_file.c static ngx_int_t ngx_conf_add_dump(ngx_conf_t *cf, ngx_str_t *filename) {off_t size;u_char *p;uint32_t hash;ngx_buf_t *buf;ngx_str_node_t *sn;ngx_conf_dump_t *cd;has…...
QEMU源码全解析 —— 内存虚拟化(23)
接前一篇文章:QEMU源码全解析 —— 内存虚拟化(22) 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM源码解析与应用》 —— 李强,机械工业出版社 QEMU内存管理模型...

【北京迅为】itop-3568 开发板openharmony鸿蒙烧写及测试-第1章 体验OpenHarmony—烧写镜像
瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工…...
TypeScript 类型声明
在 TypeScript 开发中简化类型声明,可以通过以下 7 种实用技巧 显著提升效率: 一、善用类型推断(30% 场景免声明) // ❌ 冗余写法 const user: { name: string; age: number } { name: Jack, age: 25 };// ✅ 自动推断ÿ…...

从0搭建Tomcat第二天:深入理解Servlet容器与反射机制
在上一篇博客中,我们从0开始搭建了一个简易的Tomcat服务器,并实现了基本的HTTP请求处理。今天,我们将继续深入探讨Tomcat的核心组件之一——Servlet容器,并介绍如何使用反射机制动态加载和管理Servlet。 1. Servlet容器的作用 S…...
【Python】yield函数
【Python】yield函数 1. yield介绍2.yield基本用法3.yield高级用法3.1 yield send() 方法3.2 yield from方法3.3 yield 和yield from叠加处理复杂情况下的叠加 4.yield主要应用场景5.总结 python官方api地址 1. yield介绍 在Python中,yield关键字主要用于生成器函…...
Android13修改多媒体默认音量
干就完了! 设置音量为最大音量,修改如下: /framework/base/media/java/android/media/AudioSystem.java/** hide */public static int[] DEFAULT_STREAM_VOLUME new int[] {4, // STREAM_VOICE_CALL7, // STREAM_SYSTEM5, // STREAM_RING-5, // STREAM_MUSIC15, // STREAM…...
nginx+keepalived负载均衡及高可用
一、环境准备 主机名ip地址备注openEuler-1 192.168.121.11(本机) 192.168.131.11(心跳连接) nginx主负载均衡调度器openEuler-2 192.168.121.12(本机) 192.168.131.12(心跳连接) n…...

SP导入智能材质球
智能材质球路径 ...\Adobe Substance 3D Painter\resources\starter_assets\smart-materials 放入之后就会自动刷新...
Kotlin语言特性(一):空安全、扩展函数与协程
Kotlin语言特性(一):空安全、扩展函数与协程 一、引言 Kotlin作为Android官方推荐的开发语言,相比Java具有诸多现代化特性。本文将重点介绍Kotlin三个最具特色的语言特性:空安全、扩展函数和协程,并结合A…...

Sqlserver安全篇之_启用TLS即配置SQL Server 数据库引擎以加密连接
官方文档 https://learn.microsoft.com/zh-cn/sql/database-engine/configure-windows/configure-sql-server-encryption?viewsql-server-ver16 https://learn.microsoft.com/zh-cn/sql/database-engine/configure-windows/manage-certificates?viewsql-server-ver15&pre…...

Python 爬虫 – BeautifulSoup
Python 爬虫(Web Scraping)是指通过编写 Python 程序从互联网上自动提取信息的过程。 爬虫的基本流程通常包括发送 HTTP 请求获取网页内容、解析网页并提取数据,然后存储数据。 Python 的丰富生态使其成为开发爬虫的热门语言,特…...

【星云 Orbit-STM32F4】07. 用判断数据尾来接收据的串口通用程序框架
【星云 Orbit-STM32F4】用判断数据尾来接收一串数据的串口通用程序框架 摘要 本文介绍了一种基于STM32F407微控制器的串口数据接收通用程序框架。该框架通过判断数据尾来实现一串数据的完整接收,适用于需要可靠数据传输的应用场景。本文从零开始,详细讲…...
授权与认证之jwt(一)创建Jwt工具类
JWT的Token要经过加密才能返回给客户端,包括客户端上传的Tokn,后端项目需要验证核 实。于是我们需要一个WT工具类,用来加密Token和验证Token的有效性。 一、导入依赖 <dependency><groupId>com.auth0</groupId><artifactId>jav…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...