<冲刺大厂之算法刷题>栈和队列(二)

简介: <冲刺大厂之算法刷题>栈和队列

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

题目描述

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

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

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

示例:

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

解释:

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


思路分析

栈的使用

参考代码

string removeDuplicates(string s) {
  string res;
  stack<char> st;
  for(char ch : s){
    if(!st.empty()&&st.top()==ch){//栈不为空,别切栈顶元素和遍历元素相当,则弹出栈 
      st.pop();
    }else{//栈为空/栈顶元素和遍历元素 
      st.push(ch);
    }
  }
  while(!st.empty()){
    res+=st.top();
    st.pop();
  }
  reverse(res.begin(),res.end());
  return res;
}

补充:直接拿字符串作为栈

string removeDuplicates(string s) {
  string res;
  for(char ch : s){
    if(res.empty()|| res.back()!= ch){
      res.push_back(ch);
    }else{
      res.pop_back();
    }
  } 
  return res;
}

STL中字符串常用方法总结

begin(),end()

size(),length()

resize()

empty()

back() :获取最后一个字符

front():获取第一个字符

push_back() 增加一个字符到末尾

pop_back() 删除最后一个字符

substr(pos,size)


150. 逆波兰表达式求值


题目描述


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


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


说明:


整数除法只保留整数部分。

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


示例 1:

输入:tokens = ["2","1","+","3","*"]
输出:9

解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

示例 2:

输入:tokens = ["4","13","5","/","+"]
输出:6

解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

示例 3:

输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22

解释:

该算式转化为常见的中缀算术表达式为:

((10 * (6 / ((9 + 3) * -11))) + 17) + 5

= ((10 * (6 / (12 * -11))) + 17) + 5

= ((10 * (6 / -132)) + 17) + 5

= ((10 * 0) + 17) + 5

= (0 + 17) + 5

= 17 + 5

= 22


逆波兰表达式介绍:


逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。


平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。

该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。

逆波兰表达式主要有以下两个优点:


去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。

适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。

思路分析

栈的使用


如果是数字则压入栈

如果是运算符,则从栈中弹出两个数字进行运算,运算结果再重新压入栈中

当数组遍历完毕后,返回栈中的最后一个数字即是结果.

665633d309b648fd8e12228f4966a5cd.gif

参考代码

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(num1+num2);
      } else if(tokens[i]=="-") {
        st.push(num1-num2);
      } else if(tokens[i]=="*") {
        st.push(num1*num2);
      } else {
        st.push(num1/num2);
      }
    }else{
      st.push(stoi(tokens[i]));
    }
  }
  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]

示例 3:

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


示例 4:

输入:nums = [9,11], k = 2
输出:[11]


示例 5:

输入:nums = [4,-2], k = 2
输出:[4]


思路分析

单调队列的使用

这道题不复杂,难点在于如何在 O(1) 时间算出每个「窗口」中的最大值,使得整个算法在线性时间完成。就需要「单调队列」这种特殊的数据结构来辅助了。

C++普通队列:

class Queue {
    void push(int n);
    // 或 enqueue,在队尾加入元素 n
    void pop();
    // 或 dequeue,删除队头元素
}

单调队列:

class MonotonicQueue {
    // 在队尾添加元素 n
    void push(int n);
    // 返回当前队列中的最大值
    int max();
    // 队头元素如果是 n,删除它
    void pop(int n);
}

实现单调队列数据结构

单调队列的 push 方法依然在队尾添加元素,但是要把前面比新元素小的元素都删掉; 要用到双端队列:deque

完整代码:

class MonotonicQueue {
  private:
    deque<int> Q;
  public:
    void push(int n) {
      while(!Q.empty() && Q.back() < n) { //把 < n的元素都压扁
        Q.pop_back();//从后面弹出..
      }
      Q.push_back(n);
    }
    int max() {
      return Q.front();//最大的便是对头元素
    }
    void pop(int n) {
      if(!Q.empty()&& Q.front()==n) { //如果窗口移除的元素= 最大值, 则单调队列中删除该元素.
        Q.pop_front();//从前面弹出.
      }
    }
};

功能演示:

image.gif

76c62d30e9bc71ebfaf44431de60642a.png


**注:**关于单调栈的原理请看:LeetCode刷题day31

参考代码

#include<bits/stdc++.h>
using namespace std;
class MyQueue { //单调队列
  public:
    deque<int> que;//使用deque来实现单调队列
    //弹出元素,比较当前队列头部的值和窗口的最左边的值(即将移除窗口) 是否相等,如果相等则弹出. 
    void pop(int value) {
      if(!que.empty()&& value == que.front()) {
        que.pop_front();
      }
    }
    //push压入元素 (保证que中元素是从大到小排列的) 
    //先将 <value的元素弹出,然后再压入value.这样que里面的顺序就是从小到大了 
    void push(int value) {
      while(!que.empty() && value > que.back()){
        que.pop_back();
      }
      que.push_back(value);
    }
    //查询当前队列里的最大值,直接返回队头元素 就OK了 
    int front() {
      return que.front();
    }
};
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
  MyQueue que;
  vector<int>  result;
  for(int i = 0;i < k;i++){//将前k个元素加入到单调队列中 
    que.push(nums[i]);
  }
  result.push_back(que.front());//记录前k个元素的 最大值
  for(int i = k; i < nums.size(); i++) {
    que.pop(nums[i-k]);//弹出窗口最左边元素  注意这里是弹出之前的窗口最左边值所以不用+1了哦. 
    que.push(nums[i]);//压入窗口右边的新元素 
    result.push_back(que.front()) ;//将当前窗口的最大值加入结果集 
  }
  return result;
}

347. 前 K 个高频元素

题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

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

示例 2:

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

思路分析

这个题考察内容:


要统计元素出现频率==>使用map

对频率排序=>使用优先队列

找出前K个高频元素

priority_queue利用max-heap(大顶堆)完成对元素的排序,这个大顶堆是以vector为表现形式的complete binary tree(完全二叉树)。

什么是堆呢?


堆是一颗完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。

如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。


所以我们可以用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。(当然如果是大根堆,那么弹出前k个元素就行.)


253b746f682f4f7991cc0d5f4d095e01.jpg


参考代码

//定义优先队列的排序规则(优先队列和常规的排序规则(如快排)正好相反
class mycomparison {
  public:
    bool operator(const pair<int,int>& l,const pair<int,int>& r) {
      return l.second > r.second;
    }
};
class Solution {
  public:
    //小顶堆
    vector<int> topKFrequent(vector<int>& nums, int 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>>, mycomparison> Q;
      //用固定大小为k的小根堆,扫到所有频率的数值
      for(unordered_map<int,int>::iterator it = map.begin(); it!= map.end(); it++){
        Q.push(*it);
        if(Q.size()>k){
          Q.pop();
        }
      } 
      //找出前k个高频元素,因为小根堆先弹出的是最小的. 
      vector<int> res(k);
      for(int i = k-1; i>=0;i--){
        res[i] = Q.top().first;
        Q.pop();
      } 
      return res;
    }
};

注:pair可看做map的一个子元素模板,可以对map进行赋值,初始化等操作.

总结

OK,今天关于栈和队列算法整理就到这里的,希望本篇文章能够帮助到大家,同时也希望大家看后能学有所获!!!

好了,我们下期见~

相关文章
|
2月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
73 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
2月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
40 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
2月前
|
算法
数据结构与算法二:栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式
这篇文章讲解了栈的基本概念及其应用,并详细介绍了中缀表达式转换为后缀表达式的算法和实现步骤。
59 3
|
2月前
|
存储 算法 定位技术
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
25 0
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
|
2月前
|
数据可视化 搜索推荐 Python
Leecode 刷题笔记之可视化六大排序算法:冒泡、快速、归并、插入、选择、桶排序
这篇文章是关于LeetCode刷题笔记,主要介绍了六大排序算法(冒泡、快速、归并、插入、选择、桶排序)的Python实现及其可视化过程。
18 0
|
2月前
|
算法 C++
【算法单调栈】 矩形牛棚(C/C++)
【算法单调栈】 矩形牛棚(C/C++)
|
8天前
|
算法
基于WOA算法的SVDD参数寻优matlab仿真
该程序利用鲸鱼优化算法(WOA)对支持向量数据描述(SVDD)模型的参数进行优化,以提高数据分类的准确性。通过MATLAB2022A实现,展示了不同信噪比(SNR)下模型的分类误差。WOA通过模拟鲸鱼捕食行为,动态调整SVDD参数,如惩罚因子C和核函数参数γ,以寻找最优参数组合,增强模型的鲁棒性和泛化能力。
|
14天前
|
机器学习/深度学习 算法 Serverless
基于WOA-SVM的乳腺癌数据分类识别算法matlab仿真,对比BP神经网络和SVM
本项目利用鲸鱼优化算法(WOA)优化支持向量机(SVM)参数,针对乳腺癌早期诊断问题,通过MATLAB 2022a实现。核心代码包括参数初始化、目标函数计算、位置更新等步骤,并附有详细中文注释及操作视频。实验结果显示,WOA-SVM在提高分类精度和泛化能力方面表现出色,为乳腺癌的早期诊断提供了有效的技术支持。
|
1天前
|
存储 算法
基于HMM隐马尔可夫模型的金融数据预测算法matlab仿真
本项目基于HMM模型实现金融数据预测,包括模型训练与预测两部分。在MATLAB2022A上运行,通过计算状态转移和观测概率预测未来值,并绘制了预测值、真实值及预测误差的对比图。HMM模型适用于金融市场的时间序列分析,能够有效捕捉隐藏状态及其转换规律,为金融预测提供有力工具。