Leetcode Practice — 栈和队列

155. 最小栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。

提示:

  • \(-2^{31}\) <= val <= \(2^{31}\) - 1

  • pop、top 和 getMin 操作总是在 非空栈 上调用

  • push, pop, top, and getMin最多被调用 \(3 * 10^4\)

  • 思路解析

因为会不断的入栈和出栈,那就要保证,不论入栈还是出栈,我时刻知道,到栈中当前位置的最小值是谁。

["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

对于上述输入,-2入栈时,最小值是-2,0入栈是,最小值是-2,-3入栈是最小值是-3

也就是我需要两个栈,一个栈用于存储元素,完成元素的push和pop操作;一个栈用于存储当前最小值,如果最小值更新就存入最小栈。

class MinStack {
public:
    MinStack() {

    }
    
    void push(int val) {
        if (val <= getMin()) {
            minStack.emplace(val);
        }
        valStack.emplace(val);
    }
    
    void pop() {
        if (valStack.empty()) {
            return;
        }
        int ret = valStack.top();
        valStack.pop();
        if (ret == getMin()) {
            minStack.pop();
        }
    }
    
    int top() {
        return valStack.top();
    }
    
    int getMin() {
        if (minStack.empty()) {
            return INT_MAX;
        }
        return minStack.top();
    }

private:
    stack<int> valStack;
    stack<int> minStack;
};

20. 有效的括号

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

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。

  • 左括号必须以正确的顺序闭合。

  • 每个右括号都有一个对应的相同类型的左括号。

  • 思路解析

将匹配项做一个map单独存储,这样子有更好的扩展性。

遇到左括号就入栈,遇到右括号,判断是否与栈顶元素配对,配上就出栈,否则就返回false。

class Solution {
public:
    bool isValid(string s) {
        stack<char> bracketsStack;
        for (auto &iter : s) {
            // 遇到左括号就入栈,遇到右括号,判断栈顶是否为其对应的左括号,如果是则出栈
            if (typeMap.count(iter) != 0) {
                bracketsStack.emplace(iter);
            } else {
                if (bracketsStack.empty() || iter != typeMap[bracketsStack.top()]) {
                    return false;
                }
                bracketsStack.pop();
            }
        }
        if (bracketsStack.empty()) {
            return true;
        }
        return false;
    }
private:
    unordered_map<char, char> typeMap = {
        {'(', ')'},
        {'[', ']'},
        {'{', '}'}
    };
};

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

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

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

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

输入:"abbaca"
输出:"ca"

输入:"abbbaca"
输出:"abaca"
  • 思路解析

string提供了两个操作

  • front:访问第一个字符
  • back:查询最后一个字符
  • pop_back:弹出尾巴字符,实现:length - 1即可
  • push_back:插入元素
string removeDuplicates(string s) {
    string res;
    for (auto &iter : s) {
        if (!res.empty() && iter == res.back()) {
            res.pop_back();
        } else {
            res.push_back(iter);
        }
    }
    return res;
}

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

给你一个字符串 s,「k 倍重复项删除操作」将会从 s 中选择 k 个相邻且相等的字母,并删除它们,使被删去的字符串的左侧和右侧连在一起。

你需要对 s 重复进行无限次这样的删除操作,直到无法继续为止。

在执行完所有删除操作后,返回最终得到的字符串。

输入:s = "deeedbbcccbdaa", k = 3
输出:"aa"
解释: 
先删除 "eee" 和 "ccc",得到 "ddbbbdaa"
再删除 "bbb",得到 "dddaa"
最后删除 "ddd",得到 "aa"
  • 思路解析

遍历到某个字符的时候,判断当前字符与栈顶字符是否相同

  • 如果不同,则直接入栈并开始计数
  • 如果相同,则判断当前栈顶元素累积了多少个该元素,如果累积的个数小于k-1,则继续累积,如果累积到了k-1个,当前又相同了,那么栈顶元素就可以弹出了。
string removeDuplicates(string s, int k) {
    stack<std::pair<char, int>> pairStack; // 每个元素存储当前的元素及有几个连续的值
    for (size_t i = 0; i < s.length(); i++) {
        if (!pairStack.empty() && pairStack.top().first == s[i]) {
            // 此时,看一下栈中是否已经有k-1个s[i]相同的元素,如果有,则pop出这k-1个,如果没有,则push进去
            if (pairStack.top().second == k - 1) {
                pairStack.pop();
            } else {
                pairStack.top().second++;
            }
        } else {
            pairStack.emplace(std::pair<char, int>(s[i], 1));
        }
    }
    string res;
    while(!pairStack.empty()) {
        while (pairStack.top().second-- > 0) {
            res += pairStack.top().first;
        }
        pairStack.pop();
    }
    reverse(res.begin(), res.end());
    return res;
}

删除字符串中出现次数 >= 2 次的相邻字符

第二次出现的时候,说明出现次数大于2了,这时候就可以删除了,同时跳过s中后续与当前字符相同的元素即可。

string removeDuplicates(string s, int k) {
    string res; // 每个元素存储当前的元素及有几个连续的值
    for (size_t i = 0; i < s.length(); ) {
        if (!res.empty() && res.back() == s[i]) {
            // 此时,说明已经出现过的字符第二次出现了
            char currChar = s[i];
            while(s[i] == currChar) {
                // 跳过s中其他相同的字符
                i++;
            }
            res.pop_back();
        } else {
            res.push_back(s[i]);
            i++;
        }
    }
    return res;
}

剑指 Offer 09. 用两个栈实现队列

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

class CQueue {
public:
    CQueue() {

    }
    
    void appendTail(int value) {
        stackIn.emplace(value);
    }
    
    int deleteHead() {
        if (stackOut.empty()) {
            while (!stackIn.empty()) {
                stackOut.emplace(stackIn.top());
                stackIn.pop();
            }
        }
        if (stackOut.empty()) {
            return -1;
        }
        auto ret = stackOut.top();
        stackOut.pop();
        return ret;
    }

private:
    stack<int> stackOut; // 输出栈,元素按照输入顺序的栈
    stack<int> stackIn; // 输入元素存放栈,与输入顺序相反的栈
};

239. 滑动窗口最大值

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

输入: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
  • 思路解析

假设窗口为[left, right],那么只要nums[i]比nums[j]小,则nums[i]就不纳入考虑范围。所以,当right进入窗口的时候,前面比nums[right]小的元素统统可以移除考虑范围了;这样子,留下的元素是从大到小有序的。

因为我们要移除比当前元素小的元素,也要获取最大的元素作为当前窗口最大值,因此,可以使用双端队列来实现。

// 移除比当前元素小的所有元素,只留下比当前元素大的元素
while (!dQ.empty() && nums[i] >= nums[dQ.back()]) {
    dQ.pop_back();
}
// 获取当前窗口最大值,放入结果中
resVec.emplace_back(nums[dQ.front()]);
// 如果最大值应该要移除了,则移除
if (dQ.front() + k <= i) {
    dQ.pop_front();
}

完整的实现代码如下:

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    deque<int> dQ;
    for (size_t i = 0; i < k; i++) {
        // 当前队列中仅保留比当前元素大的元素的位置,队列中下标对应的元素由大到小
        while (!dQ.empty() && nums[i] >= nums[dQ.back()]) {
            dQ.pop_back();
        }
        dQ.emplace_back(i);
    }
    vector<int> resVec = {nums[dQ.front()]};
    for (size_t i = k; i < nums.size(); i++) {
        // 当前队列中仅保留比当前元素大的元素的位置
        while (!dQ.empty() && nums[i] >= nums[dQ.back()]) {
            dQ.pop_back();
        }
        dQ.emplace_back(i);
        if (dQ.front() <= i - k) {
            dQ.pop_front();
        }
        resVec.emplace_back(nums[dQ.front()]);
    }
    return resVec;
}
 

需要完整版练习笔记,可关注公众号后台私信~~

原文链接:https://www.cnblogs.com/shuezhang/p/17277372.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Leetcode Practice — 栈和队列 - Python技术站

(0)
上一篇 2023年4月17日
下一篇 2023年4月17日

相关文章

  • C语言线性表全面梳理操作方法

    C语言线性表全面梳理操作方法 线性表概述 线性表是一种常用的数据结构,是指数据元素之间存在一定逻辑顺序,每个元素都有唯一的前驱和后继。 线性表有两种存储方式: 顺序存储结构 和 链式存储结构。 顺序存储结构 顺序存储结构是指采用顺序存储方式存储线性表,即将线性表的元素依次存储在一段连续的存储空间内。 代码示例:创建顺序存储线性表 #define MaxSiz…

    数据结构 2023年5月17日
    00
  • python聚类算法选择方法实例

    Python聚类算法选择方法实例 聚类是一种无监督学习方法,它将相似的数据点分组到一起。在本攻略中,我们将介绍如何选择适合的聚类算法来处理不同类型的数据。 步骤1:了解聚类算法 在选择聚类算法之前,我们需要了解不同类型的聚类算法。在本攻略中,我们将介绍两种常见的聚类算法:K均值聚类和层次聚类。 K均值聚类 K均值聚类是一种基于距的聚类算法,它将数据点分成K个…

    python 2023年5月14日
    00
  • C语言数据结构图的创建与遍历实验示例

    下面是“C语言数据结构图的创建与遍历实验示例”的完整攻略。 1. 创建数据结构图 1.1 创建图对象 首先需要创建一个图对象,可以使用邻接矩阵或邻接表来表示图。使用邻接矩阵表示时,将所有顶点的编号按照一定顺序排列在矩阵的行和列上,使用0或1表示两个顶点之间是否有边。使用邻接表表示时,需要一个array存储所有的顶点,数组中的每个元素包含一个链表,链表中存储与…

    数据结构 2023年5月17日
    00
  • 决策树的python实现方法

    以下是关于“决策树的Python实现方法”的完整攻略: 简介 决策树是一种常用的机器学习算法,用于分类和回归问题。在本教程中,我们将介绍决策树的原理和Python实现方法,并提供两个示例。 原理 决策树是一种基于树形结构的分类模型,它通过对数据集进行划分,构建一棵树来实现分类。决策树的构建过程包括选择最优特征、划分数据集、递归构建子树等步骤。在分类时,决策树…

    python 2023年5月14日
    00
  • 图计算引擎分析–GridGraph

    作者:京东科技 李永萍 GridGraph:Large-Scale Graph Processing on a Single Machine Using 2-Level Hierarchical Partitioning 图计算框架 图计算系统按照计算方式划分可分为:单机内存图处理系统,单机核外图处理系统,分布式内存图处理系统,分布式核外图处理系统。本文将详…

    算法与数据结构 2023年4月20日
    00
  • C语言程序设计第五版谭浩强课后答案(第二章答案)

    首先,需要说明的是本题涉及到一个特定的知识领域,即C语言程序设计,以及该领域内某个具体教材的课后习题解答。因此,本攻略的重心将放在如何利用Markdown格式对该领域内的知识进行准确、清晰的表达和展示上。 下面是本攻略的目录: C语言程序设计第五版谭浩强课后答案(第二章答案)攻略 一、简介 二、题目列表 三、示例说明 示例一 示例二 四、总结 一、简介 本攻…

    数据结构 2023年5月17日
    00
  • python实现梯度法 python最速下降法

    下面是详细讲解“Python实现梯度法和最速下降法”的完整攻略。 梯度法 梯度法是一种常用的优化算法用于求解无约束优化问题。其基本思想是每一步代中,沿着当前的梯度方向进行下降,以望找到函数的最小值点。 下面是一个Python实现梯度法的示例: import numpy as np def gradient_descent(f, df, x0, alpha=0…

    python 2023年5月14日
    00
  • Huffman实现

    Huffman编码树 秒懂:【算法】Huffman编码_哔哩哔哩_bilibili 约定:字符x的编码长度 就是其对应叶节点的深度; 在一个字符集中,每个字符出现的次数有多有少,那么若都采用固定长度编码的话,那么编码长度会非常大,并且搜索时间复杂度都非常高;若采用非固定编码,出现次数多的字符编码长度小一些,并且放在树深度小的地方,提高搜索时间效率;这样带权平…

    算法与数据结构 2023年4月17日
    00
合作推广
合作推广
分享本页
返回顶部