☰
力扣刷题C++实践:栈、队列与堆的应用解析
2026/10/28 14:29:49 网站建设 项目流程

1. 项目概述:力扣刷题的C++实践记录

最近在整理自己的力扣刷题笔记时,发现C++解法往往能提供独特的视角和高效的实现。这篇日记将分享我在解决150(逆波兰表达式求值)、239(滑动窗口最大值)、347(前K个高频元素)和71(简化路径)这四道题目时的思考过程和代码实现。这些题目覆盖了栈、队列、堆等数据结构的典型应用场景,对准备技术面试很有帮助。

2. 题目解析与实现思路

2.1 150. 逆波兰表达式求值

逆波兰表达式(后缀表达式)的计算是栈结构的经典应用。遇到数字就压栈,遇到运算符就弹出栈顶两个元素进行计算,再将结果压回栈中。

int evalRPN(vector<string>& tokens) { stack<int> st; for (const auto& token : tokens) { if (token == "+" || token == "-" || token == "*" || token == "/") { int b = st.top(); st.pop(); int a = st.top(); st.pop(); if (token == "+") st.push(a + b); else if (token == "-") st.push(a - b); else if (token == "*") st.push(a * b); else st.push(a / b); } else { st.push(stoi(token)); } } return st.top(); }

注意:除法的处理要特别注意,题目保证除法结果会向零截断,所以直接使用整数除法即可。

2.2 239. 滑动窗口最大值

这道题要求我们在O(n)时间复杂度内解决,普通的暴力解法无法满足要求。这里使用双端队列维护一个单调递减的队列:

vector<int> maxSlidingWindow(vector<int>& nums, int k) { deque<int> dq; vector<int> res; for (int i = 0; i < nums.size(); ++i) { // 移除超出窗口范围的元素 if (!dq.empty() && dq.front() == i - k) dq.pop_front(); // 维护单调递减队列 while (!dq.empty() && nums[dq.back()] < nums[i]) dq.pop_back(); dq.push_back(i); // 窗口形成后记录结果 if (i >= k - 1) res.push_back(nums[dq.front()]); } return res; }

2.3 347. 前K个高频元素

这道题考察堆的使用。我们可以先用哈希表统计频率,然后用最小堆来维护前K个高频元素:

vector<int> topKFrequent(vector<int>& nums, int k) { unordered_map<int, int> freq; for (int num : nums) freq[num]++; auto cmp = [&freq](int a, int b) { return freq[a] > freq[b]; }; priority_queue<int, vector<int>, decltype(cmp)> pq(cmp); for (auto& p : freq) { pq.push(p.first); if (pq.size() > k) pq.pop(); } vector<int> res; while (!pq.empty()) { res.push_back(pq.top()); pq.pop(); } return res; }

2.4 71. 简化路径

这道题需要处理Unix风格的路径字符串,使用栈来处理路径中的"."和"..":

string simplifyPath(string path) { vector<string> st; stringstream ss(path); string dir; while (getline(ss, dir, '/')) { if (dir.empty() || dir == ".") continue; if (dir == "..") { if (!st.empty()) st.pop_back(); } else { st.push_back(dir); } } string res; for (const auto& s : st) { res += "/" + s; } return res.empty() ? "/" : res; }

3. 解题技巧与优化思路

3.1 数据结构的选择与比较

在解决这些问题时,数据结构的选择至关重要:

  1. 栈:适合处理对称性、递归性问题(如150题)
  2. 队列/双端队列:适合处理滑动窗口、BFS相关问题(如239题)
  3. 堆:适合处理Top K问题(如347题)
  4. 哈希表:适合快速查找和频率统计

3.2 C++标准库的高效使用

C++标准库提供了丰富的数据结构实现,合理使用可以大幅提升编码效率:

  • std::stack和std::queue是适配器容器,底层默认使用deque
  • std::priority_queue默认是大顶堆,可以通过自定义比较函数实现小顶堆
  • std::unordered_map比std::map有更好的平均时间复杂度(O(1) vs O(log n))

3.3 边界条件处理

在刷题过程中,边界条件的处理往往决定成败:

  • 空输入的处理
  • 极端值(如最大/最小值)的处理
  • 特殊字符或格式的处理
  • 循环终止条件的判断

4. 常见问题与调试技巧

4.1 调试技巧

  1. 打印中间结果:在关键步骤后打印变量值
  2. 使用断言:验证不变量和前提条件
  3. 单元测试:为每个函数编写测试用例

4.2 性能优化

  1. 减少不必要的拷贝:使用引用传递参数
  2. 预分配内存:对于已知大小的容器,提前reserve
  3. 选择合适的数据结构:根据操作特点选择最优结构

4.3 编码风格建议

  1. 变量命名要有意义
  2. 适当添加注释解释复杂逻辑
  3. 保持函数短小精悍,单一职责
  4. 错误处理要明确

5. 力扣刷题的系统性方法

5.1 题目分类训练

建议按照题目类型进行集中训练,比如:

  • 数组与字符串
  • 链表
  • 树与图
  • 动态规划
  • 贪心算法

5.2 错题本与复盘

建立自己的错题本,记录:

  1. 初次解题的思路
  2. 遇到的坑和错误
  3. 最优解的分析
  4. 同类题目的变形

5.3 时间管理与模拟面试

在准备面试时:

  1. 限时练习(如30分钟一题)
  2. 模拟白板编程
  3. 练习口头解释思路

6. C++刷题的特殊考量

6.1 语言特性利用

  1. 移动语义:减少不必要的拷贝
  2. 智能指针:管理动态内存
  3. Lambda表达式:简化回调实现

6.2 标准库算法

熟练使用<algorithm>中的常用函数:

  • sort/stable_sort
  • lower_bound/upper_bound
  • next_permutation
  • accumulate

6.3 内存与性能

  1. 注意容器扩容的成本
  2. 避免不必要的动态内存分配
  3. 考虑缓存友好性

刷题不仅是解决问题的过程,更是培养计算思维和编程习惯的好方法。通过系统地记录和复盘,可以显著提升算法能力和编码水平。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询