算法学习打卡day41|栈和队列:栈和队列相互实现、括号匹配、逆波兰表达式、滑动窗口最大值问题、求前 K 个高频元素

news2024/12/23 16:17:30

栈和队列相互实现

力扣题目链接:用栈实现队列、用队列实现栈
题目描述:

  • 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
    实现 MyQueue 类:
    void push(int x) 将元素 x 推到队列的末尾
    int pop() 从队列的开头移除并返回元素
    int peek() 返回队列开头的元素
    boolean empty() 如果队列为空,返回 true ;否则,返回 false
  • 请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
    实现 MyStack 类:
    void push(int x) 将元素 x 压入栈顶。
    int pop() 移除并返回栈顶元素。
    int top() 返回栈顶元素。
    boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

思路:

  • 用栈实现队列的思路是遇到 push 就放到stk1里,如果遇到pop那么就把stk1的元素一次存到stk2里,然后从stk2的栈顶pop,就可以实现先入先出了。
  • 用队列实现栈的思路是一个队列que1用来存储元素,用另外一个队列que2来备份元素,但要pop元素时,就把que1的元素都存到que2的元素里,然后把que1最后一个元素删掉就行。

代码实现:

  • 用栈实现队列
class MyQueue {
public:
    MyQueue() {
    }
    
   void push(int x) {
        stack1.push(x);
   }
   int pop() {
       int x = 0;
       if (!stack2.empty()) {
           x = stack2.top();
           stack2.pop();
           return x;
       }
       
       while (!stack1.empty()) {
           x = stack1.top();
           stack1.pop();
           stack2.push(x);
       }
       x = stack2.top();
       stack2.pop();
       return x;
   }
   int peek() {
        int result = this->pop();//降低代码耦合度
        stack2.push(result);
        return result;
   }
   bool empty() {
       return stack1.empty() && stack2.empty();
   }
private:
    stack<int> stack1; //用来压栈
    stack<int> stack2;  //从stack1出占,压入stack2就是栈顶元素了
};
  • 用队列实现栈
class MyStack {
public:
    queue<int> que1;
    MyStack() {
    }
    void push(int x) {
        que1.push(x);
    }
    int pop() {
        int size = que1.size();
        int x = 0;
        while (--size) {
            x = que1.front();
            que1.pop();
            que1.push(x);
        }
        int result = que1.front();
        que1.pop();
        return result;
    }
    int top() {
        int result = pop();
        que1.push(result);
        return result;
    }
    bool empty() {
        return que1.empty();
    }
    
};

括号匹配

力扣题目链接

题目描述:
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。

示例 1
输入:s = “()”
输出:true
示例 2
输入:s = “()[]{}”
输出:true
示例 3
输入:s = “(]”
输出:false

思路:

  • 这道题因为所有的元素都是括号,所以,我们直接把符号压入栈中,然后遇到右括号就和栈顶元素比较,如果匹配不到(不匹配或栈为空都是无效)那么肯定是无效的。当字符串遍历完后如果栈非空,那么字符串也是无效的。
  • 另外奇数一定是无法匹配的,可以在开头判断一下。

代码实现:

bool isValid(string s) {
        if (s.size() % 2)   return false;   //奇数一定无法匹配
        stack<char> stk;
        for (char& i : s) {
            if (i == '{' || i == '(' || i == '[') {
                stk.push(i);//左括号入栈
            } else {
                if (stk.empty())    return false;//如果是右括号,但是此时栈空,说明右边多了直接return
                if (i == ')') {
                    if (stk.top() != '(')   return false;//匹配不到就return
                } else if (i == ']') {
                    if (stk.top() != '[')   return false;
                } else {
                    if (stk.top() != '{')   return false;
                }
                stk.pop();//匹配到了,别忘了pop
            }
        }
        return stk.empty();//不用单独if判断了,直接在这里判断就行
    }
  • 代码实现进行了一定的优化,遇到左括号可以直接存对应右括号,那么遇到右括号可以直接进行比较是否相同了。
bool isValid(string s) {
        if (s.size() % 2)   return false;   //奇数一定无法匹配
        stack<char> stk;
        for (char& i : s) {
            if (i == '{')   stk.push('}');//换个思路,存右括号,那么遇到右括号直接比较是否相等就行了
            else if (i == '(') stk.push(')');
            else if (i == '[')  stk.push(']');
            else if (stk.empty() || stk.top() != i) return false;//右边多了或者没匹配到
            else stk.pop();//匹配到了,别忘了pop
        }
        return stk.empty();//不用单独if判断了,直接在这里判断就行
    }
  • 应用场景:
    • 编译器在词法分析的过程中处理括号、花括号等这个符号的逻辑,就是使用了栈来进行匹配的。
    • linux的cd命令也涉及到栈对路径处理。
    • 函数调用时的调用栈。
    • 递归也会借助栈来实现,每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中。

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

力扣题目链接
题目描述:
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

示例:
输入:“abbaca”
输出:“ca”
解释:
例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。

思路:

  • 这道题有两种写法,栈的实现思路比较简单。
  • 借助栈:如果栈为空或者当前元素和栈顶元素不相等,就入栈,如果相等就把栈顶元素删除,遍历结束后就得到了最终结果。注意:字符串和数组是天然的栈!!!
  • 双指针法: 双指针法就是原地移除元素了,定义一个left和right指针,别激动和普通的删除重复项还不一样,是慢指针和快指针一起走,而且不是right和left比较,而是right和left-1去比较。! 而一般双指针法是left在从0走,right从1走,
    • 只有当慢指针的前一个元素和当前值相等时再退一格,只有这样才能保证奇数时留一个,偶数时都干掉(奇数时和前一个元素不一样了)。
    • 然后赋值时不是++left而是left++了,因为如果是偶数,那就是回退,然后偶数序列右边界下一个元素一定和左边界前一个不相等,这时就left就直接覆盖都给删了,如果是奇数那在右边界的时候已经和左边界前一个元素不相等了,可以留一个

代码实现:

string removeDuplicates(string s) {
        string result;
        for (char& i : s) {
            if (result.empty() || i != result.back()) {
                result.push_back(i);
                continue;
            }  
            result.pop_back();
        }
        return result;
    }
  • 双指针法
string removeDuplicates(string s) {
        int left = 0, right = 0;
        for (right; right < s.size(); ++right) {
            if (left > 0 && s[left - 1] == s[right]) {
                left--;
            } else {
                 s[left++] = s[right];
            }
        }
        s.resize(left);
        return s;
    }

逆波兰表达式

力扣题目链接
题目描述:
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。

注意:
有效的算符为 ‘+’、‘-’、‘*’ 和 ‘/’ 。
每个操作数(运算对象)都可以是一个整数或者另一个表达式。
两个整数之间的除法总是 向零截断 。
表达式中不含除零运算。
输入是一个根据逆波兰表示法表示的算术表达式。
答案及所有中间计算结果可以用 32 位 整数表示。

示例 1

输入:tokens = [“2”,“1”,“+”,“3”,“*”]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

思路:

  • 这个题很简单,遇到加减乘除就从栈里取两个元素,然后将计算结果再放入栈里即可,没有遇到运算符就把其他元素放入栈。

代码实现:

int evalRPN(vector<string>& tokens) {
        stack<int> stk;
        int num1, num2;
        for (string& s : tokens) {
            if (s == "+" || s == "-" || s == "*" || s == "/") {
                num1 = stk.top();
                stk.pop();
                num2 = stk.top();
                stk.pop();
                if (s == "+")   stk.push(num2 + num1);
                if (s == "-")   stk.push(num2 - num1);
                if (s == "*")   stk.push(num2 * num1);
                if (s == "/")   stk.push(num2 / num1);
            } else {
                stk.push(stoi(s));
            }
        }
        return stk.top();
    }

滑动窗口最大值问题

力扣题目链接
题目描述:
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

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

示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
在这里插入图片描述

思路:

  • 这道题涉及到单调队列的应用,需要自己实现一个单调队列,什么是单调队列? 即让队列里的元素单调递增或者递减。
  • 我们这道题需要实现一个单调递减的队列,最大值在队列头部,这里采用deque实现,C++底层默认也是deque实现的,主要涉及到push和pop两个操作:
    • push:push的时候如果需要push的值比栈尾大时,就把尾部的值删掉(如果不用删除的情况,就存到另一个栈里,push后再放进去)直到push的value比尾部值小停止删除。
    • pop:pop的话,需要和队列的front元素比较,如果相等就pop(),否则不需要进行操作,因为最大值没有发生改变,不需要执行pop操作(其实在push的时候已经给它干掉了)。
    • front:这个就是返回队列开始的元素,记录窗口最大值。

代码实现:

class Solution {
public:
    //定义单调队列
    class MyQueue {
    public:
        void push(int value) {
            while (!que.empty() && value > que.back()) {
                que.pop_back();
            }
            que.push_back(value);
        }
        void pop(int value) {
            if (!que.empty() && value == que.front()) {
                que.pop_front();
            }
        }
        int front() {
            return que.front();
        }
    private:
        deque<int> que;
    };
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyQueue my_que;
        //先放入k - 1个元素
        for (int i = 0; i < k - 1; ++i) {
            my_que.push(nums[i]);
        }
        int i = k - 1;
        vector<int> results;
        //依次取最大值
        while (i < nums.size()) {
            my_que.push(nums[i]);
            results.push_back(my_que.front());
            my_que.pop(nums[i - k + 1]);
            i++; 
        }
        return results;
    }
};

求前 K 个高频元素

力扣题目链接
题目描述:
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

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

思路:

  • 一般遇到前k个或者k个元素合并之类的都可以试着用优先级队列来解题,本题也是使用优先级队列。
  • 分为以下三步:
    1. 首先先统计数组所有元素出现的次数,使用哈希map
    2. 建立小根堆,一直维持k个最大元素即可,既然是要求高频元素为啥不用大根堆? 因为使用大根堆需要比较map中的所有元素,而我们本题只需要维持最大的k个元素即可,每次利用小根堆把最小的元素干掉。
    3. 输出优先级队列的元素。
  • 另外本体需要自定义优先级队列的cmp,由于第三个参数是个类参数,所以我们需要自己定义一个类。
  • 还有为什么左大于右就会建立小顶堆,而不是建立大顶堆?

例如我们在写快排的cmp函数的时候,return left>right 就是从大到小,return left<right 就是从小到大,优先级队列的定义正好反过来了,可能和源码实现有关。

代码实现:

class mycomparison {
    public:
        bool operator () (const pair<int, int>& a, const pair<int, int>& b) {
            return a.second > b.second;
        }
    }; 
    vector<int> topKFrequent(vector<int>& nums, int k) {
        //统计次数
        unordered_map<int, int> maps;
        for (int& i : nums) {
            maps[i]++;
        }
        //建堆
        priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;
        for (auto& i : maps) {
            pri_que.push(i);
            if (pri_que.size() > k) {
                pri_que.pop();
            }
        }

        //输出结果
        vector<int> results;
        while (k--) {
            results.push_back(pri_que.top().first);
            pri_que.pop();
        }
        return results;
    }

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

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

相关文章

2 任务2: 使用趋动云GPU进行猫狗识别实践

使用趋动云GPU进行猫狗识别实践 1 创建项目2 初始化开发环境3 调试代码4 提交离线任务5 结果集存储与下载 使用趋动云提供的免费GPU&#xff0c;进行猫狗识别实践。 虽然例程里面提供的是基于tensorflow的&#xff0c;但是你也可以使用pytorch的代码 使用这个平台的一个优点就是…

1-前端基本知识-CSS

1-前端基本知识-CSS 文章目录 1-前端基本知识-CSS总体概述什么是CSS&#xff1f;CSS引入方式行内式内嵌式连接式/外部样式表 CSS选择器元素选择器id选择器class选择器&#xff08;使用较广&#xff09; CSS浮动CSS定位静态定位&#xff1a;static绝对定位&#xff1a;absolute相…

“精准分割视频,误差降低至零——视频剪辑的新革命!”

在数字媒体的时代&#xff0c;视频剪辑已经成为我们日常生活和工作中不可或缺的一部分。无论是制作一部电影、剪辑一个纪录片&#xff0c;还是编辑一个家庭视频&#xff0c;我们都需要一个精准、高效的视频剪辑工具。今天&#xff0c;我们向您推荐一款全新的视频剪辑软件——精…

[NLP] 使用Llama.cpp和LangChain在CPU上使用大模型

一 准备工作 下面是构建这个应用程序时将使用的软件工具: 1.Llama-cpp-python 下载llama-cpp, llama-cpp-python [NLP] Llama2模型运行在Mac机器-CSDN博客 2、LangChain LangChain是一个提供了一组广泛的集成和数据连接器&#xff0c;允许我们链接和编排不同的模块。可以常…

关于卷积神经网络的步幅(stride)

认识步幅&#xff08;stride&#xff09; 卷积核从输入数组的最左上方开始&#xff0c;按从左往右、从上往下的顺序&#xff0c;依次在输入数组上滑动&#xff0c;我们将每次滑动的行数和列数称为步幅。 计算步幅 假设输入的形状n∗n&#xff0c;卷积核的形状为f∗f&#xff0…

域渗透06-协议(NTLM hash利用)

前言&#xff1a; 当我们获取到一台域内主机打算干什么&#xff0c;毫无疑问当然是拿域控&#xff0c;如果域控未发现漏洞应该怎么办&#xff0c;首先我们需要查看我们拿到主机的权限和在域中的组&#xff0c;如果本机权限够我们就需要利用工具抓取本机的hash&#xff0c;然后…

HCIE-CCE

1、创建集群 svc网络&#xff0c;10.247 pod网络&#xff0c;10.244 节点网络&#xff0c;192.168.66&#xff08;master和node一致&#xff09; 2、创建节点 上面集群选择了最新版本1.27&#xff0c;CCE从1.27版本开始不再支持docker容器引擎&#xff0c;仅支持containered&…

渗透实战靶机3wp

0x00 简介 目标IP&#xff1a;xxxx.95 测试IP&#xff1a;xxxx.96 测试环境&#xff1a;win10、kali等 测试时间&#xff1a;2021.7.23-2021.7.26 测试人员&#xff1a;ruanruan 0x01 信息收集 1、端口扫描 21&#xff0c;ftp&#xff0c;ProFTPD&#xff0c;1.3.3c22&a…

Oracle 安装及 Spring 使用 Oracle

参考内容&#xff1a; docker安装oracle数据库史上最全步骤&#xff08;带图文&#xff09; Mac下oracle数据库客户端 Docker安装Oracle docker能安装oracle吗 Batch script for add a auto-increased primary key for exist table with records Docker 安装 Oracle11g 注意&a…

基于单片机的甲醛检测器设计

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式 文章目录 概要 一、设计的主要内容二、系统硬件设计三、软件设计4.1 程序结构流程图原理图 四、结论五、 文章目录 概要 本文将要提…

餐饮软件开发价格受到需求的影响!

随着科技的进步和互联网的普及&#xff0c;餐饮行业也逐渐实现了数字化转型&#xff0c;越来越多的餐厅开始引入餐饮软件来提升服务质量、提高效率、拓展销售渠道等。 而对于许多初创餐厅或餐饮企业来说&#xff0c;开发一款适合自己的餐饮软件的价格一直是他们关注的焦点&…

NtripShare Caster高精度定位CORS服务软件

NtripShare CORS是NtripShare GNSS系列软件中最早的软件系统&#xff0c;也是NtripShare名称的起源。 所谓GNSS CORS服务系统一般构成&#xff1a; 1&#xff09;基准站网&#xff1a;由若干个分布合理的GNSS 基准站组成&#xff1b; 2&#xff09;数据传输系统&#xff1a;…

基于springboot垃圾分类管理系统

基于springboot垃圾分类管理系统 摘要 垃圾分类管理系统是一个基于现代技术和数据管理方法的解决方案&#xff0c;旨在协助城市和社区更有效地管理垃圾分类。在这个系统中&#xff0c;Spring Boot框架充当了后端应用程序的构建工具&#xff0c;为其提供了高度灵活的特性。该系统…

运算符与运算表达式

运算符的结合性和优先级&#xff1a; 结合性&#xff1a; 所有的单目运算符、条件运算符、赋值运算符及扩展运算符&#xff0c;结合方向都是从右向左&#xff0c;其余运算符的结合方向是从左向右。 优先级&#xff1a; 初等运算符>单目运算符>算数运算符&#xff08;…

卡牌游戏类型定制开发微信卡牌小程序游戏

卡牌类型的游戏开发具有一些独特的特点和挑战&#xff0c;以下是一些主要的特点&#xff1a; 卡牌设计和平衡&#xff1a;卡牌游戏的核心是卡牌设计和平衡。开发团队需要设计各种卡牌&#xff0c;确保它们在游戏中相互平衡&#xff0c;以便提供有趣的游戏体验。卡牌的特性、效…

UE5 新特性 Nanite 开启

啥也不说&#xff0c;只能说&#xff0c;真的牛&#xff0c;在自己的项目上&#xff0c;从10几20的帧数&#xff0c;直接彪到了70 适用场景&#xff1a; 大场景&#xff0c;三角面足够多 在Project Setting里面 将这几个勾未true 勾上这个&#xff0c;放入场景即可

小程序如何设置自取模式下的服务方式

设置自取模式下的服务方式是非常重要的&#xff0c;尤其是对于到店自取和到店堂食这两种不同的服务模式。下面我们就来介绍一下如何在小程序中设置这两种服务方式。 在小程序管理员后台->配送设置处&#xff0c;在服务方式处&#xff0c;设置自取情况下的服务方式。默认是&…

变量环境、变量提升和暂时性死区

JavaScript中的提升 在JavaScript中&#xff0c;“Hoisting”&#xff08;提升&#xff09;是一种特性&#xff0c;它将变量和函数的声明移动到作用域的顶部。这意味着可以在声明之前使用这些变量和函数&#xff0c;而不会报错。 当JavaScript代码执行时&#xff0c;会经过两个…

计算机网络基础知识1

1、tcp三次握手&#xff1f; SYN&#xff0c;标志位&#xff0c;用于建立TCP连接的握手过程中的标志位。 ACK&#xff0c;确认位&#xff0c;用于说明整个包是确认报文。 TCP/IP协议是传输层的一个面向连接提供可靠安全的传输协议。第一次握手有客户端发起&#xff0c;客户端向…

超详细的厦门旅游攻略!暑期旅游特种兵必备

随着暑期的旅游越来越火爆&#xff0c;许多旅行社纷纷向大家推荐了许多热门景点&#xff0c;其中厦门旅游是许多人暑假首选的地点&#xff0c;你知道这些宣传图片或旅游攻略是如何制作出来的吗&#xff1f; 今天为大家推荐一款能够快速制作出厦门旅游攻略的软件——boardmix博思…