news 2026/7/23 2:36:35

代码随想录day5

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
代码随想录day5

很久没做题更新博客了,最近在做项目与复习八股,每次被面试横向后都要摆烂一段时间

栈与队列

1.用栈实现队列

力扣题目链接(opens new window)

使用栈实现队列的下列操作:

push(x) -- 将一个元素放入队列的尾部。
pop() -- 从队列首部移除元素。
peek() -- 返回队列首部的元素。
empty() -- 返回队列是否为空。

class MyQueue { public: stack<int> stIn; stack<int> stOut; MyQueue() { } void push(int x) {//放在队尾,即放在in栈的栈顶 stIn.push(x); } int pop() {//循环取出,out栈为空时,循环取出in栈放入out栈中,取出out栈栈顶元素 if(stOut.empty()){ while(!stIn.empty()){ stOut.push(stIn.top()); stIn.pop(); } } int result=stOut.top(); stOut.pop(); return result; } int peek() { if(stOut.empty()){ while(!stIn.empty()){ stOut.push(stIn.top()); stIn.pop(); } } int result=stOut.top(); return result; } bool empty() { return stIn.empty()&&stOut.empty(); } };

这里使用的是两个栈来模拟一个队列的方法,使用一个in栈一个out栈来代替队列的入口与出口,

入栈操作就只需要压入in栈就可以了

出栈时,若out栈为空,就要将in栈内的元素循环压入到out栈之中,之后弹出out栈的栈顶就可以了,out栈不为空,就直接弹出out栈栈顶元素。

获得栈顶元素,入出栈是一样的操作,只是不需要弹出,获得返回就可以了

判空,两个栈都为空即为队列为空。

2.用队列实现栈

力扣题目链接(opens new window)

使用队列实现栈的下列操作:

  • push(x) -- 元素 x 入栈
  • pop() -- 移除栈顶元素
  • top() -- 获取栈顶元素
  • empty() -- 返回栈是否为空
class MyStack { public: queue<int> q; MyStack() { } void push(int x) {//直接放入队尾 q.push(x); } int pop() {//弹出栈顶元素,即队尾元素 int size=q.size(); size--; while(size--){//将队尾元素移动到队首 q.push(q.front()); q.pop(); } int result=q.front(); q.pop(); return result; } int top() { int size=q.size(); size--; while(size--){ q.push(q.front()); q.pop(); } int result=q.front(); q.push(result);//恢复原来结构 q.pop(); return result; } bool empty() { return q.empty(); } };

这里使用的是一个队列来模拟栈的操作

入栈,直接放入队列就可以了。

出栈,计算出队列的长度,循环将队首元素放入队尾就可以,次数为长度-1,然后获得队首元素并弹出。

获得栈顶元素,与出栈类似,不过不弹出队首元素,需要再取出放入队尾,恢复原来的结构。

判空,直接返回队列是否为空。

3.有效的括号

力扣题目链接(opens new window)

给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。
  • 注意空字符串可被认为是有效字符串。
class Solution { public: stack<char> st; bool isValid(string s) { for(int i=0;i<s.size();i++){ if(s[i]=='('){ st.push(')'); }else if(s[i]=='{'){ st.push('}'); }else if(s[i]=='['){ st.push(']'); }else if(st.empty()||s[i]!=st.top()){//右括号多了或者括号不匹配 return false; }else{ st.pop(); } } return st.empty();//栈为空说明全部匹配有效,不为空说明左括号多了 } };

这里使用栈来进行解决,遍历字符串,遇到左边括号就在栈内放入一个对应的右括号,反之,三种情况

1.括号不匹配,即此时遇到的右括号与栈顶不匹配,说明左右括号不匹配

2.栈为空,说明右括号更多,左括号不够的情况

3.说明括号可以匹配的上,弹出栈内元素

最后遍历结束后,若栈内不为空,说明左括号多了,不匹配,为空说明刚好匹配成功。

4.删除字符串中的所有相邻重复项

力扣题目链接(opens new window)

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

class Solution { public: stack<char> st; string removeDuplicates(string s) { for(int i=0;i<s.size();i++){ if(st.empty()||s[i]!=st.top()){ st.push(s[i]); }else{ st.pop(); } } string res=""; while(!st.empty()){ res+=st.top(); st.pop(); } reverse(res.begin(),res.end()); return res; } };

这里也是使用栈来解决,遍历字符串,栈为空或者栈顶与字符不相等的时候,就加入到栈内,相反,栈不为空且栈顶元素与字符相等的时候,说明相邻重复,就要弹出栈顶元素。

遍历结束后,循环取出栈顶字符,得到的字符串反转就可以得到删除后的字符串。

5.逆波兰表达式求值

力扣题目链接(opens new window)

根据 逆波兰表示法,求表达式的值。

有效的运算符包括 + , - , * , / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明:

整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

class Solution { public: int evalRPN(vector<string>& tokens) { stack<int> st; for(int i=0;i<tokens.size();i++){ if(tokens[i]=="+"||tokens[i]=="-"||tokens[i]=="*"||tokens[i]=="/"){ int num1=st.top(); st.pop(); int num2=st.top(); st.pop(); if(tokens[i]=="+") st.push(num2+num1); if(tokens[i]=="-") st.push(num2-num1); if(tokens[i]=="*") st.push(num2*num1); if(tokens[i]=="/") st.push(num2/num1); }else{ st.push(stoi(tokens[i])); } } int res=st.top(); return res; } };

这里也是用栈来解决,遍历字符串数组,将字符串转换成数字stoi(tokens[i])存入栈内,遇到运算符就进行计算,将栈顶的两个数字进行运算符计算,得到的值再加入到栈中,遍历结束后,栈内剩余的数就是最后结果。

6.滑动窗口最大值

力扣题目链接(opens new window)

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

class Solution { private: class Myque{//使用双端队列自定义实现一个单调队列,出口按照由大到小 public: deque<int> que; void pop(int value){//弹出元素 if(!que.empty()&&value==que.front()){//不为空并且与出口值相等时才弹出 que.pop_front();//弹出出口元素,即最大值 } } void push(int value){//压入新元素 while(!que.empty()&&value>que.back()){//从入口处开始比较,将更小的弹出 que.pop_back(); } que.push_back(value);//队尾入口处放入新元素 } int front(){ return que.front();//返回出口元素,即最大值 } }; public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { Myque que; vector<int> res; for(int i=0;i<k;i++){ que.push(nums[i]); } res.push_back(que.front()); for(int i=k;i<nums.size();i++){ que.pop(nums[i-k]);//将窗口内第一个移除 que.push(nums[i]);//放入新元素 res.push_back(que.front());//返回最大值 } return res; } };

这里每次移动窗口的时候就要获得窗口内的最大值,如果直接对窗口内元素进行排序的话,复杂度太高,这里使用单调队列来解决

即加入元素的时候,比入口的元素值小,就直接放在队尾,如果比入口元素值大,则直接删除队尾中小于这个元素的值,直到比入口元素小或者队列为空,这样就保证了队列的队首为最大值,后面的元素也是按照由大到小排列的。

使用双端队列deque实现单调队列这个数据结构

出队操作,即移动窗口把最大值移出去了,就弹出队首元素,否则就不变。

入队操作,即移动窗口时加入新元素,新元素比队尾值大就移除队尾的值,直到队尾元素比新元素大,并放入新元素到队尾。

获取队首,直接返回双端队列的首部元素,即为最大值。

具体做题,遍历k个元素,都加入到单调队列中,获得此时最大值加入到返回数组中,之后继续遍历之后的元素,先移除窗口内第一个元素,再新元素加入到单调队列中,最后将最大值即队首元素加入到返回数组中。

7.前 K 个高频元素

力扣题目链接(opens new window)

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

  • 输入: nums = [1,1,1,2,2,3], k = 2
  • 输出: [1,2]

示例 2:

  • 输入: nums = [1], k = 1
  • 输出: [1]

提示:

  • 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
  • 你的算法的时间复杂度必须优于 $O(n \log n)$ , n 是数组的大小。
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
  • 你可以按任意顺序返回答案。
class Solution { public: vector<int> topKFrequent(vector<int>& nums, int k) { vector<int> res(k); unordered_map<int,int> map; for(int i=0;i<nums.size();i++){ map[nums[i]]++; } //使用小顶堆,这样队列中只需要存入k个元素,大顶堆就要全部存入 priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> que;//小顶堆 for(auto& it:map){ que.push({it.second,it.first});//first为频率,second为元素 if(que.size()>k){//只维护前k个 que.pop(); } } for(int i=k-1;i>=0;i--){//逆序存入 res[i]=que.top().second; que.pop(); } return res; } };

这里因为要求前k个高频元素,实际上就是先对数组中元素的出现次数进行记录,之后排序,求出前k个元素,这里其实就是求第k元素,可以考虑使用堆来实现

因为要第k大,就要考虑使用小顶堆,这样堆中只需要存放k个元素,空间消耗更小

如果使用大顶堆,堆中就要存放全部的元素,空间消耗高。

先使用unordered_map来存入元素以及出现次数,再构建小顶堆,最后再存入。

priority_queue<存放类型,存放容器,比较方法>

存放类型为pair<int,int> 使用的比较方式为greater<pair<int,int>>,容器是vector<pair<int,int>>

遍历map,元素存入堆中,由于greater默认比较的是第一个元素,所以这里存放的是{it.second,it.first},注意只维护k个元素,超出就弹出,最后剩下的就是前k大的元素。由小到大排列。

之后创建大小为k的数组,逆序存入堆顶中元素que.top().second。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/23 2:35:10

反射性回忆循环:提升技术长期记忆的认知策略与实践指南

在长期记忆研究中&#xff0c;如何将短期接触的信息转化为持久、可检索的知识是一个核心挑战。传统的学习方法往往侧重于即时输入&#xff0c;却忽略了信息在记忆系统中的巩固和整合过程。反射性回忆循环&#xff08;Reflective Recall Cycle&#xff09;作为一种认知策略&…

作者头像 李华
网站建设 2026/7/23 2:34:55

Grok for Excel:金融建模与图表生成的智能助手实战指南

这类工具最值得先看的不是功能列表&#xff0c;而是能不能在普通 Excel 环境里稳定跑起来&#xff0c;以及它到底解决了金融建模和图表生成中的哪些具体痛点。Grok for Excel 上线后&#xff0c;很多人第一反应是“能不能替代 VBA 或 Python 脚本”&#xff0c;但实测下来&…

作者头像 李华
网站建设 2026/7/23 2:32:20

腾讯云GPU实例配置指南:从选型到深度学习环境部署实践

在云计算和人工智能快速发展的背景下&#xff0c;算力尤其是 GPU 算力的需求呈现爆发式增长。企业自建 GPU 集群面临成本高、运维复杂、技术迭代快等挑战&#xff0c;而公有云提供的弹性 GPU 服务成为许多团队的首选。腾讯云作为国内主要的云服务商之一&#xff0c;其 GPU 实例…

作者头像 李华
网站建设 2026/7/23 2:30:22

能科科技AI+工业场景化应用【第三期】:AI表单识别对比应用案例

Q&#xff1a;在日常工作中&#xff0c;您的团队是否仍在手工处理堆积如山的手写工单、检验报告等各类单据和图纸&#xff1f; 这种重复性劳动不仅效率低下、成本高昂&#xff0c;更因人为差错导致数据无法准确、及时地流入业务系统&#xff0c;形成信息孤岛和瓶颈&#xff0c…

作者头像 李华
网站建设 2026/7/23 2:29:57

收窄 LLM 决策空间

一、「收窄决策空间」收窄的是什么 保留 LLM 的决策权–LLM 负责工具选择、语义理解、答案组织。在LLM决策前&#xff0c;工程侧压缩候选集、参数、上下文。 这一层的主线是LLM 决策空间越小&#xff0c;行为越稳定。 二、提示词工程&#xff1a;能力有边界 遇到准确性问题&…

作者头像 李华
网站建设 2026/7/23 2:29:42

C++左值右值引用与移动语义:从概念到实战性能优化

1. 项目概述&#xff1a;从“值”的分类说起在C的世界里&#xff0c;尤其是从C11标准开始&#xff0c;“左值”和“右值”这两个概念从一个相对边缘的语法细节&#xff0c;一跃成为了理解现代C高效编程的核心钥匙。很多朋友在面试或者阅读开源库源码时&#xff0c;看到一堆带&a…

作者头像 李华