代码随想录18

20. 有效的括号

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

有效字符串需满足:

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

示例 1:

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

示例 2:

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

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

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

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

cpp 复制代码
#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. 栈中的内容即为最终结果

    • 遍历完成后,栈中的字符就是所有消除了相邻重复字符后剩下的字符。
    • 注意:栈中的字符顺序是从栈底到栈顶,而实际字符串顺序是从左到右,因此需要反转栈中的内容以得到最终结果。
cpp 复制代码
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;
    }
};

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

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

代码如下:

cpp 复制代码
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. 注意事项

    • 运算顺序:对于减法和除法,弹出的第一个数字是右操作数,第二个数字是左操作数。
    • 操作数和操作符的输入顺序必须符合逆波兰表达式的规则。
cpp 复制代码
#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),会导致在大数据量情况下性能不佳。

cpp 复制代码
#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)。以下是推荐的高效实现:

cpp 复制代码
#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;
    }
};
相关推荐
小冯的编程学习之路40 分钟前
【LeetCode】:解数独【困难】
算法·leetcode·职场和发展
DC_BLOG1 小时前
数据结构排序
java·数据结构·算法·排序算法
G_qingxin1 小时前
前端排序算法
前端·算法·排序算法
大丈夫立于天地间1 小时前
OSPF - LSA对照表
网络·网络协议·学习·算法·信息与通信
Vec[95]2 小时前
如何将光源视角的深度贴图应用于摄像机视角的渲染
c++·算法·3d·贴图
23级二本计科3 小时前
排序算法的实现(插入,希尔,选择,冒泡,堆排,快排)
算法·排序算法
8Qi83 小时前
多目标优化算法——基于聚类的不规则Pareto前沿多目标优化自适应进化算法(CA-MOEA)
人工智能·算法·多目标优化·进化计算·群体智能·ca-moea
Swift社区4 小时前
LeetCode - #180 Swift 实现连续数字查询
算法·leetcode·swift
黑客K-ing4 小时前
如何安全保存用户密码及哈希算法
算法·哈希算法
最好Tony4 小时前
深度学习blog-Meanshift均值漂移算法-最大熵模型
深度学习·算法·均值算法