From a0ddd919b805e0bdcba85d2cd26705e12893e393 Mon Sep 17 00:00:00 2001 From: Bacon_up_everyday Date: Sun, 21 Sep 2025 22:33:27 +0800 Subject: [PATCH 1/2] 20250921 --- topic01/submit/.gitkeep | 117 ++++++++++++++++++ ..._\346\233\276\345\237\271\346\272\220.cpp" | 61 +++++++++ 2 files changed, 178 insertions(+) create mode 100644 "topic01/submit/LC1996_\346\233\276\345\237\271\346\272\220.cpp" diff --git a/topic01/submit/.gitkeep b/topic01/submit/.gitkeep index e69de29..9d0d849 100644 --- a/topic01/submit/.gitkeep +++ b/topic01/submit/.gitkeep @@ -0,0 +1,117 @@ +# 学习笔记:LeetCode 1944「队列中可以看到的人数」 +(题目链接:[https://leetcode.cn/problems/number-of-visible-people-in-a-queue/](https://leetcode.cn/problems/number-of-visible-people-in-a-queue/),参考提交记录:2024年前后常见AC解法) + + +## 一、题意与问题模型 +### 1. 题目核心需求 +给定长度为`n`的互不相同的身高数组`heights`(第`i`个元素表示第`i`个人的身高),返回数组`answer`,其中`answer[i]`表示**第`i`个人在右侧队列中能看到的人数**。 + +### 2. 可见条件的关键解读 +第`i`个人能看到第`j`个人(`i < j`)的核心条件: +`min(heights[i], heights[j]) > max(heights[i+1], ..., heights[j-1])` +可转化为更直观的逻辑: +- 若`heights[i] < heights[j]`:第`i`人能看到第`j`人,且第`j`人会**挡住`j`右侧所有比`heights[j]`矮的人**(因为这些人比`heights[j]`矮,更比`heights[i]`矮,会被`j`遮挡); +- 若`heights[i] > heights[j]`:第`i`人能看到第`j`人,但`j`右侧比`heights[j]`高的人仍有可能被`i`看到(需继续判断)。 + +由于身高**互不相同**,无需处理“相等”的边界 case,简化逻辑。 + + +## 二、核心解法:单调递减栈(从右往左遍历) +### 1. 解法思路推导 +要计算第`i`人能看到的右侧人数,需重点关注**右侧第一个比`heights[i]`高的人**(记为`right_max`): +- `i`能看到`right_max`左侧所有比`heights[i]`矮的人(这些人未被`right_max`遮挡); +- `i`还能看到`right_max`本身(若存在)。 + +单调递减栈的作用就是**高效维护右侧身高的“遮挡关系”**: +- 栈中存储的是「右侧人员的索引」,且对应身高满足**从栈顶到栈底单调递减**(栈顶是离`i`最近的、身高较矮的人,栈底是更远的、身高较高的人); +- 从右往左遍历(先处理`i`右侧的人,再处理`i`),确保栈中已包含`i`右侧所有相关人员的信息。 + +### 2. 具体步骤 +1. **初始化**:空栈`stack`(存索引)、结果数组`answer`(长度`n`,初始为0); +2. **遍历**:从`i = n-1`(最右侧的人)向左遍历至`i=0`: + - 「弹出计数」:若栈非空且`heights[i] > heights[栈顶索引]`,说明栈顶对应的人能被`i`看到,弹出栈顶并将`answer[i] += 1`(直到栈顶身高 > `heights[i]`,停止弹出); + - 「补充计数」:若弹出后栈仍非空,说明存在“右侧第一个比`heights[i]`高的人”(栈顶),`answer[i] += 1`(能看到这个人); + - 「入栈维护」:将当前`i`压入栈,维持栈的单调递减性; +3. **返回结果**:遍历结束后,`answer`即为所求。 + + +## 三、代码实现与逐行注释 +```cpp +#include +#include +#include +using namespace std; + +class Solution { +public: + vector canSeePersonsCount(vector& heights) { + int n = heights.size(); + vector answer(n, 0); // 结果数组,初始所有位置能看到0人 + stack st; // 单调递减栈,存右侧人员的索引 + + // 从右往左遍历(先处理右侧,再处理当前i) + for (int i = n - 1; i >= 0; --i) { + // 步骤1:弹出栈中所有比heights[i]矮的人(这些人能被i看到) + while (!st.empty() && heights[i] > heights[st.top()]) { + st.pop(); // 弹出:该人已被计数,且不会再影响左侧的人 + answer[i]++; // 每弹出一个,能看到的人数+1 + } + // 步骤2:若栈非空,说明栈顶是右侧第一个比heights[i]高的人(能被i看到) + if (!st.empty()) { + answer[i]++; + } + // 步骤3:将当前i入栈,维护栈的单调递减性(供左侧的人判断) + st.push(i); + } + return answer; + } +}; + +// 测试示例(对应题目示例1) +int main() { + vector heights = {10, 6, 8, 5, 11, 9}; + Solution sol; + vector res = sol.canSeePersonsCount(heights); + // 预期输出:3 1 2 1 1 0 + for (int num : res) { + cout << num << " "; + } + return 0; +} +``` + + +## 四、复杂度分析 +| 维度 | 复杂度 | 分析 | +|------------|--------|----------------------------------------------------------------------| +| 时间复杂度 | O(n) | 每个元素仅入栈1次、出栈1次(栈的总操作次数为`2n`),遍历过程为`O(n)`,整体线性。 | +| 空间复杂度 | O(n) | 最坏情况(身高严格递增,如`[1,2,3,4,5]`):栈需存储所有`n`个元素,空间为`O(n)`。 | + + +## 五、示例验证(以题目示例1为例) +输入:`heights = [10,6,8,5,11,9]`,输出:`[3,1,2,1,1,0]` +逐元素推导(从右往左): +- `i=5`(身高9):栈空 → `answer[5]=0`,入栈`[5]`; +- `i=4`(身高11):栈顶5(9)<11 → 弹出,`answer[4]=1`;栈空 → 入栈`[4]`; +- `i=3`(身高5):栈顶4(11)>5 → `answer[3]=1`,入栈`[4,3]`; +- `i=2`(身高8):栈顶3(5)<8 → 弹出,`answer[2]=1`;栈顶4(11)>8 → `answer[2]=2`,入栈`[4,2]`; +- `i=1`(身高6):栈顶2(8)>6 → `answer[1]=1`,入栈`[4,2,1]`; +- `i=0`(身高10):栈顶1(6)<10 → 弹出,`answer[0]=1`;栈顶2(8)<10 → 弹出,`answer[0]=2`;栈顶4(11)>10 → `answer[0]=3`,入栈`[4,0]`。 + +最终结果与示例一致,验证解法正确性。 + + +## 六、常见误区与改进建议 +### 1. 常见误区 +- **遍历方向错误**:从左往右遍历无法高效维护“右侧遮挡关系”,会导致重复判断,时间复杂度退化到`O(n²)`(超时); +- **栈的单调性错误**:用“单调递增栈”会错误维护身高关系,导致计数漏算或多算; +- **忘记补充计数**:弹出后栈非空时,未加1(漏掉“右侧第一个更高的人”)。 + +### 2. 改进与扩展 +- **提前剪枝**:若`heights`是严格递减数组(如`[5,4,3,2,1]`),每个`i`只能看到`i+1`,可直接优化,但对整体复杂度无影响; +- **输出具体可见的人**:若题目要求返回第`i`人能看到的具体索引,可将栈改为存储“索引+身高”的结构体,弹出时记录索引; +- **举一反三**:该解法可推广到“下一个更大元素”“每日温度”等单调栈经典问题,核心是利用栈维护“前后元素的依赖关系”。 + + +## 七、一句话总结 +本题的核心是用**单调递减栈**从右往左遍历,通过“弹出矮元素计数、保留高元素补计数”的逻辑,高效维护右侧遮挡关系,最终在`O(n)`时间内解决问题,是单调栈在“前后依赖关系”类问题中的典型应用。 \ No newline at end of file diff --git "a/topic01/submit/LC1996_\346\233\276\345\237\271\346\272\220.cpp" "b/topic01/submit/LC1996_\346\233\276\345\237\271\346\272\220.cpp" new file mode 100644 index 0000000..eec8b0c --- /dev/null +++ "b/topic01/submit/LC1996_\346\233\276\345\237\271\346\272\220.cpp" @@ -0,0 +1,61 @@ +#include +#include +#include +using namespace std; +class Solution { +public: + vector canSeePersonsCount(vector& heights) { + stacka; + + int n = heights.size(); + vectorb(n); + + for (int i = n - 1; i >= 0; i--) { + while (!a.empty() && heights[i] >heights[ a.top()]) { + a.pop(); + b[i]++; + } + if (!a.empty()) { + b[i]++; + } + a.push(i); + } + return b; + } + }; + +int main() { + vectorm = { 10,6,8,5,11,9 }; + Solution sol; + vector result=sol.canSeePersonsCount(m); + for (int num : result) { + cout << num << " "; + } + + return 0; +} + + + + + +/* +Ľⷨݼջ +### 1. ⷨ˼·Ƶ +Ҫ`i`ܿҲصע * *Ҳһ`heights[i]`ߵ** Ϊ`right_max` +- `i`ܿ`right_max`б`heights[i]`ˣЩδ`right_max`ڵ +- `i`ܿ`right_max`ڣ + +ݼջþ * *ЧάҲߵġڵϵ * * +- ջд洢ǡҲԱҶӦ * *ջջ׵ݼ * *ջ`i`ġ߽ϰˣջǸԶġ߽ϸߵˣ +- ȴ`i`Ҳˣٴ`i`ȷջѰ`i`ҲԱϢ + +### 2. 岽 +1. * *ʼ * *ջ`stack ``answer``n`ʼΪ0 +2. * * * *`i = n - 1`Ҳˣ`i=0` +- ջǿ`heights[i] > heights[ջ]`˵ջӦܱ`i`ջ`answer[i] += 1`ֱջ > `heights[i]`ֹͣ +- ջԷǿգ˵ڡҲһ`heights[i]`ߵˡջ`answer[i] += 1`ܿˣ +- ջάǰ`i`ѹջάջĵݼԣ +3. * *ؽ * *`answer`Ϊ + +*/ \ No newline at end of file -- Gitee From f26c6188003ee33b0175f18171a98b0d8904707a Mon Sep 17 00:00:00 2001 From: Bacon_up_everyday Date: Sun, 21 Sep 2025 23:23:51 +0800 Subject: [PATCH 2/2] 20250921 --- ..._\346\233\276\345\237\271\346\272\220.cpp" | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 "topic01/submit/Leetcode42_\346\233\276\345\237\271\346\272\220.cpp" diff --git "a/topic01/submit/Leetcode42_\346\233\276\345\237\271\346\272\220.cpp" "b/topic01/submit/Leetcode42_\346\233\276\345\237\271\346\272\220.cpp" new file mode 100644 index 0000000..da07fd6 --- /dev/null +++ "b/topic01/submit/Leetcode42_\346\233\276\345\237\271\346\272\220.cpp" @@ -0,0 +1,62 @@ +#include +using namespace std; +#include +#include +class Solution { +public: + int trap(vector& height) { + + stacka; + int sum = 0; + for (int i = 0; i < height.size(); i++) { + while (!a.empty() && height[a.top()] < height[i]) { + int ltop = a.top(); + a.pop(); + + if (a.empty()) { break; } + int left = a.top(); + int right = i; + int wide = right -left - 1; + int heights = min(height[left], height[right]) - height[ltop]; + sum += wide * heights; + + } + a.push(i); + + } + return sum; + } +}; +int main() { + vectorm = { 0,1,0,2,1,0,1,3,2,1,2,1 }; + Solution sol; + int n = sol.trap(m); + cout << n; + return 0; +} + + +/* +1. ʼ׼ +ջѡ񣺴洢ӵǸ߶ȣΪҪͨ ۿȡұ߽ˮƽ룩 +sum ۼа۵Ĵˮ +ӣұÿӣжǷγɰۡ +2. ֺ̣ij +ÿ i ʱݵǰӸ߶ջӦ߶ȵĹϵ + 1ǰ߶ ջ߶ ջάֵݼ + height[i] height[ջ]˵ǰ޷Ϊ ұ߽硱ߣ޷հۣ i ջ +ĿģΪδ DZڵײ ߽硱άջĵݼԡ + 2ǰ߶ > ջ߶ ۼ + height[i] > height[ջ]˵ҵ ұ߽硱ǰ iջӦ ײײµջ ߽硱ʼ㰼۴ˮ + 1ȡ ۵ײ¼ջ bottom = ջȻ󵯳ջײȷջ߽磩 + 2жǷ ߽硱ײջΪգ˵û߽磨ûиߵӣ޷γɷհֱۣѭ㣩 + 3ȷ ߽硱 㰼۲ +߽磺ײµջ left = ջ߶ > ײ߶ȣ߽Ҫ󣩣 +ұ߽磺ǰ right = i߶ > ײ߶ȣұ߽Ҫ󣩣 +ۿȣwidth = right - left - 1ȥ 1 Ϊұ߽籾ռ 1 λãмľǰ۵ˮƽȣ +Ч߶ȣheight_diff = min(height[left], height[right]) - height[bottom]ұ߽нϰǸ ˮ߶ȡټȥײ߶Ⱦʵܴˮĸ߶ȣ + 4ۼӴˮǰ۴ˮ = Ч߶ȣ sum С +ѭжϣ굱ǰۺ󣬼жϵǰ߶ջ߶ȵĹϵܴڶǶװۣظ㣩ֱջջǰ߶ ջ߶ȡ +3. ܴˮ +Ӻsum а۴ˮܺͣ sum ɡ +*/ \ No newline at end of file -- Gitee