代码随想录18

news2025/1/8 15:21:54

20. 有效的括号

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

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。
  • 注意空字符串可被认为是有效字符串。

示例 1:

  • 输入: "()"
  • 输出: true

示例 2:

  • 输入: "()[]{}"
  • 输出: true

思路:这道题在以前编译原理课设制作编译器的时候遇到过

很明显这道题要用栈的思路先进后出

注意点:就是输入左边的符号的时候要把相应右边的符号压入栈,这样方便比较

#include <stack>
#include <string>
using namespace std;

class Solution {
public:
    bool isValid(string s) {
        stack<char> st; // 初始化栈
        
        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() || st.top() != s[i]) {
                    return false; // 不匹配直接返回 false
                }
                st.pop(); // 匹配成功,弹出栈顶元素
            }
        }
        
        // 最后检查栈是否为空,若为空说明所有括号匹配成功
        return st.empty();
    }
};

这是我自己写的代码:

思路:

  1. 栈的特点:后进先出
    栈的特点使得它可以快速判断当前字符是否与前一个字符相同,从而决定是否需要移除字符。

  2. 逐一遍历字符串
    遍历字符串的每个字符,对每个字符进行如下操作:

    • 如果栈为空,直接将字符压入栈中。
    • 如果栈非空,检查栈顶字符是否与当前字符相同:
      • 若相同:表示找到一对相邻重复字符,将栈顶字符弹出,移除这对字符。
      • 若不同:将当前字符压入栈中。
  3. 栈中的内容即为最终结果

    • 遍历完成后,栈中的字符就是所有消除了相邻重复字符后剩下的字符。
    • 注意:栈中的字符顺序是从栈底到栈顶,而实际字符串顺序是从左到右,因此需要反转栈中的内容以得到最终结果。

class Solution {
public:
    string removeDuplicates(string s) {
        stack<char> st; // 修正 "stacck" 为 "stack<char>"
        for (int i = 0; i < s.size(); i++) {
            if (st.empty() || st.top() != s[i]) { // 修正 "st==empty()" 为 "st.empty()",并且修正 "st.top" 为 "st.top()"
                st.push(s[i]);
            } else { // 如果字符与栈顶字符相同
                st.pop();
            }
        }

        // 将栈中的字符还原为字符串
        string result = "";
        while (!st.empty()) {
            result += st.top();
            st.pop();
        }
        reverse(result.begin(), result.end()); // 栈中字符顺序为倒序,需要反转
        return result;
    }
};

老师的做法的话就不用反转字符串

用字符串来模拟栈的话就可以直接找到字符串的顶和尾,并且弹出和压入

代码如下:

class Solution {
public:
    string removeDuplicates(string s) {
        string result = ""; // 用字符串模拟栈
        for (char c : s) {
            if (!result.empty() && result.back() == c) { // 如果栈顶与当前字符相同
                result.pop_back(); // 弹出栈顶
            } else {
                result.push_back(c); // 压入当前字符
            }
        }
        return result;
    }
};

50. 逆波兰表达式求值

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

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

说明:

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

示例 1:

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

示例 2:

  • 输入: ["4", "13", "5", "/", "+"]
  • 输出: 6
  • 解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

以下是我自己写的思路:

没想到的:

使用 std::stoi 将其转换为整数

  1. 逆波兰表达式的基本特点

    • 遇到数字时,将其压入栈中。
    • 遇到操作符时,从栈中弹出两个数字,进行计算,将计算结果重新压入栈。
  2. 步骤

    1. 遍历表达式列表 tokens
    2. 如果当前元素是数字:
      • 使用 std::stoi 将其转换为整数,压入栈中。
    3. 如果当前元素是操作符:
      • 弹出栈顶的两个数字,假设为 ba
      • 根据操作符执行计算:a + b, a - b, a * b, 或 a / b
      • 将计算结果压入栈中。
    4. 遍历结束后,栈顶元素即为最终结果。
  3. 注意事项

    • 运算顺序:对于减法和除法,弹出的第一个数字是右操作数,第二个数字是左操作数。
    • 操作数和操作符的输入顺序必须符合逆波兰表达式的规则。

#include <stack>
#include <vector>
#include <string>
#include <cstdlib> // for stoi

using namespace std;

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] != "/") {
                // 是数字,转换为整数并压入栈
                st.push(stoi(tokens[i]));
            } else {
                // 是操作符,弹出两个操作数
                int b = st.top(); st.pop();
                int a = st.top(); st.pop();
                
                // 执行相应的操作并将结果压入栈
                if (tokens[i] == "+") st.push(a + b);
                else if (tokens[i] == "-") st.push(a - b);
                else if (tokens[i] == "*") st.push(a * b);
                else if (tokens[i] == "/") st.push(a / b);
            }
        }
        // 返回最终结果
        return st.top();
    }
};

239. 滑动窗口最大值

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

返回 滑动窗口中的最大值 

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

示例 2:

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

下面是我自己写的

这个用数组的代码,代码时间复杂度为 O(n×k)O(n \times k)O(n×k),会导致在大数据量情况下性能不佳。

#include <vector>
#include <algorithm>
using namespace std;

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> result; // 存储结果
        
        // 检查边界条件
        if (nums.empty() || k <= 0) {
            return result;
        }

        for (int i = 0; i <= nums.size() - k; i++) {
            // 在每个滑动窗口中找到最大值
            int temp = nums[i];
            for (int j = 1; j < k; j++) {
                temp = max(temp, nums[i + j]);
            }
            result.push_back(temp);
        }

        return result;
    }
};

优化后的代码(使用双端队列提高效率):

如果你需要更高效的实现,可以用双端队列将复杂度优化到 O(n)O(n)O(n)。以下是推荐的高效实现:

#include <vector>
#include <deque>
using namespace std;

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> result;       // 存储结果
        deque<int> dq;            // 存储滑动窗口中可能的最大值下标

        for (int i = 0; i < nums.size(); i++) {
            // 移除窗口外的元素
            if (!dq.empty() && dq.front() < i - k + 1) {
                dq.pop_front();
            }

            // 移除队列中小于当前元素的所有元素
            while (!dq.empty() && nums[dq.back()] < nums[i]) {
                dq.pop_back();
            }

            // 添加当前元素下标
            dq.push_back(i);

            // 记录当前窗口的最大值
            if (i >= k - 1) {
                result.push_back(nums[dq.front()]);
            }
        }

        return result;
    }
};

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

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

相关文章

最新最详细的配置Node.js环境教程

配置Node.js环境 一、前言 &#xff08;一&#xff09;为什么要配置Node.js&#xff1f;&#xff08;二&#xff09;NPM生态是什么&#xff08;三&#xff09;Node和NPM的区别 二、如何配置Node.js环境 第一步、安装环境第二步、安装步骤第三步、验证安装第四步、修改全局模块…

PHP框架+gatewayworker实现在线1对1聊天--接收消息(7)

文章目录 接收消息的原理接收消息JavaScript代码 接收消息的原理 接收消息&#xff0c;就是接受服务器转发的客户端消息。并不需要单独创建函数&#xff0c;因为 ws.onmessage会自动接收消息。我们需要在这个函数里进行处理。因为初始化的时候&#xff0c;已经处理的init类型的…

当算法遇到线性代数(四):奇异值分解(SVD)

SVD分解的理论与应用 线性代数系列相关文章&#xff08;置顶&#xff09; 1.当算法遇到线性代数&#xff08;一&#xff09;&#xff1a;二次型和矩阵正定的意义 2.当算法遇到线性代数&#xff08;二&#xff09;&#xff1a;矩阵特征值的意义 3.当算法遇到线性代数&#xff0…

科研绘图系列:R语言科研绘图之标记热图(heatmap)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理画图系统信息参考介绍 科研绘图系列:R语言科研绘图之标记热图(heatmap) 加载R包 library(tidyverse) library(ggplot2) library(reshape)…

Mysql--基础篇--SQL(DDL,DML,窗口函数,CET,视图,存储过程,触发器等)

SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;是用于管理和操作关系型数据库的标准语言。它允许用户定义、查询、更新和管理数据库中的数据。SQL是一种声明性语言&#xff0c;用户只需要指定想要执行的操作&#xff0c;而不需要详细说明如何…

Excel重新踩坑5:二级下拉列表制作;★数据透视表;

0、在excel中函数公式不仅可以写在单元格里面&#xff0c;还可以写在公式里面。 1、二级下拉列表制作&#xff1a; 2、数据透视表&#xff1a; 概念&#xff1a;通过拖拉就能实现复杂函数才能实现的数据统计问题。 概览&#xff1a;在插入选项中有个数据透视表&#xff0c;数…

Linux-----进程处理(waitpid,进程树,孤儿进程)

目录 waitpid等待 进程树 孤儿进程 waitpid等待 Linux中父进程除了可以启动子进程&#xff0c;还要负责回收子进程的状态。如果子进程结束后父进程没有正常回收&#xff0c;那么子进程就会变成一个僵尸进程——即程序执行完成&#xff0c;但是进程没有完全结束&#xff0c;其…

解决报错net.sf.jsqlparser.statement.select.SelectBody

在我们项目集成mybatis-plus时,总会遇到奇奇怪怪的报错,比如说下面的这个报错 而这个报错,是告诉我们的分页依赖冲突,要加个jsqlparser依赖来解决这个冲突,也相当于平衡,但是可能因为我们版本的不匹配,还是会报错,例如下面这样 但是我们是不知道到底是什么依赖冲突的,这个时候就…

感知器的那些事

感知器的那些事 历史背景Rosenblatt和Minsky关于感知机的争论弗兰克罗森布拉特简介提出感知器算法Mark I感知机争议与分歧马文明斯基简介单层感知器工作原理训练过程多层感知器工作原理单层感知机 vs 多层感知机感知器模型(Perceptron),是由心理学家Frank Rosenblatt在1957年…

内核链表 例题 C语言实现

问题&#xff1a; 将下面的数据节点信息转换为链表结构&#xff0c;并遍历输出。要求根据type的值来决定val的类型。 type为1代表bool类型&#xff0c;2代表整形&#xff0c;3代表浮点型。无需解析文本&#xff0c;直接赋值形成节点即可。 代码&#xff1a; list.c #includ…

C语言结构体数组

上次我们讲解了结构体&#xff0c;这里还有高级应用就是结构体数组&#xff08;集合的集合&#xff09; &#xff08;这里提醒一句&#xff0c;想要在北京参加NCRE考试的朋友们今天开始报名了&#xff09; 定义 还是拿上回那个学生数据的结构体 typedef struct {int year;i…

深入了解 ES6 Map:用法与实践

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

在Vue3项目中使用svg-sprite-loader

1.普通的svg图片使用方式 1.1 路径引入 正常我们会把项目中的静态资源放在指定的一个目录&#xff0c;例如assets,使用起来就像 <img src"../assets/svgicons/about.svg" /> 1.2封装组件使用 显然上面的这种方法在项目开发中不太适用&#xff0c;每次都需…

谷歌SEO真的需要很长时间吗?

关键在于策略与执行力&#xff0c;很多人在刚开始做谷歌SEO时&#xff0c;都会产生一种挫败感&#xff0c;觉得排名变化太慢&#xff0c;看不到显著效果。这其实是因为SEO本身是一项需要时间的工作&#xff0c;特别是在竞争激烈的领域。但如果策略得当、执行力强&#xff0c;时…

耐高压26V输入5V升压充电8.4V芯片

HU6877作为一款集成了26V高耐压保护的5V升压至8.4V两节锂电池充电管理IC&#xff0c;凭借其高效升压、智能充电管理、多重安全保护及高耐压特性&#xff0c;在高端手电筒、便携式医疗设备、无人机等领域展现出了广泛的应用前景。本文将详细探讨HU6877的技术特点、工作原理、应用…

linuxCNC(六)配置LinuxCNC完成伺服控制

这里写目录标题 1、 cia402安装2、找伺服描述文件&#xff08;xml&#xff09;3、配置ethercat-config.xml3.1、打开hal-cia402/example/ethercat-conf.xml3.2、修改 ethercat-conf.xml文件中vid"0x000116c7" pid"0x003e0402"3.3、其他参数&#xff0c;根据…

【数据结构】链表(2):双向链表和双向循环链表

双向链表&#xff08;Doubly Linked List&#xff09; 定义&#xff1a; 每个节点包含三个部分&#xff1a; 数据域。前驱指针域&#xff08;指向前一个节点&#xff09;。后继指针域&#xff08;指向下一个节点&#xff09;。 支持从任意节点向前或向后遍历。 #define dat…

指针 const 的组合

1、首先来了解一下常量 const int num 5&#xff1b; 那么num的值是5&#xff0c; num的值不可修改 2、来了解一下指针 int value 5; int* p &value; 我喜欢吧指针和类型放一起&#xff0c;来强调p是一个指针类型&#xff0c; 而赋值的时候就得赋值一个int类型的地址…

Tableau数据可视化与仪表盘搭建-数据可视化原理

目录 内容 做个小实验 数据如何变成图表 1 2 维度和度量定义 3 度量映射图形&#xff0c;维度负责区分 1 可映射的数据类型 2 可视化字典 3 使用Tableau将数据变成图表(Tableau可视化原理) 1 2 拖拽 3 具体操作 4 总结 内容 点击左下角的工作表 tableau可以自动…

【WRF数据准备】气象驱动数据-ERA5是否需要单层位势数据?

目录 气象驱动数据-ERA5是否需要单层位势(Geopotential)数据?位势(Geopotential)输入的重要性Vtable的管理参考气象驱动数据-ERA5是否需要单层位势(Geopotential)数据? 本博客参考WRF论坛中讨论内容-How to use ERA5 Data From Copernicus Database,总结位势(Geopot…