[算法学习]堆栈篇:7个案例带你玩转栈、队列、堆

栈、队列、堆的学习

文章目录

    • 栈、队列、堆的学习
      • 栈、队列、堆 - 基础知识回顾
        • 队列
      • 7道经典题目
        • 1. 使用队列实现栈(easy)(栈、队列)
        • 2. 使用栈实现队列(easy)(栈、队列)
        • 3. 包含min函数的栈(easy)(栈)
        • 4. 合法的出栈序列(medium)(栈、队列)
        • 5. 简单的计算器(hard)(栈)
        • 6. 数组中第K大的数(easy)(堆)
        • 7. 寻找中位数(hard)(堆)

栈、队列、堆 - 基础知识回顾

先进后出的线性表

#include <stack>

stack<int> stk;

stk.top() : 取出栈顶元素,返回引用
stk.empty() : 判断栈是否为空,空 返回 true
stk.push(x) : 将x添加到栈
stk.pop() : 弹出栈顶,返回为空
stk.size() : 返回栈存储元素个数
队列

先进先出的线性表

#include <queue>

queue<int> que;

que.front() : 返回队列头部元素
que.back() : 返回队列尾部元素
que.empty() : 判断队列是否为空
que.push(x) : 将x添加到队列
que.pop() : 弹出队列头部元素
que.size() : 返回队列存储元素个数

非线性数据结构,有两个直接后继。可被视为一颗完全二叉树的数组对象,且堆中某个节点的值总是不大于或不小于其父节点的值。其中,根节点最大的称为最大堆,最小的称为最小堆

#include <queue>

std::priority_queue<int> big_heap; //默认构造方法:最大堆
std::priority_queue<int, std::vector<int>, std::greater<int>> small_heap; //最小堆构造方法
std::priority_queue<int, std::vector<int>, std::less<int>> big_heap; //最大堆构造方法

big_heap.empty() : 判断堆是否为空
big_heap.push(x) : 将元素添加到二叉堆
big_heap.pop() : 弹出堆顶元素
big_heap.top() : 返回堆顶元素
big_heap.size() : 返回堆元素个数

7道经典题目

1. 使用队列实现栈(easy)(栈、队列)

题目说明:
设计一个栈,支持基本的栈的操作,栈的内部存储数据结构为队列

目的:更好的了解栈和队列的性质

class MyStack {
private:
    std::queue<int> _data;
public:
    MyStack(){}
    void push(int x){ //思路可参照汉诺塔问题,不过由于队列两头都可以出,所以会简单一点
        std::queue<int> temp_queue;
        temp_queue.push(x);
        while (!_data.empty()){
            temp_queue.push(_data.front());
            _data.pop();
        }
        while (temp_queue.empty()){
            _data.push(temp_queue.front());
            temp_queue.pop();
        }
    }
    void pop(){
        _data.pop();
        return;
    }
    int top(){
        return _data.front();
    }
    bool empty(){
        return _data.empty();
    }
};
2. 使用栈实现队列(easy)(栈、队列)

题目说明:
设计一个队列,支持基本的队列的操作,队列的内部存储数据结构为栈

目的:更好的了解栈和队列的性质

class MyQueue {
private:
    std::stack<int> _data;
public:
    MyQueue(){}
    void push(int x){
        std::stack<int> temp_stack;
        while (!_data.empty()){
            temp_stack.push(_data.top());
            _data.pop();
        }
        _data.push(x);
        while (temp_stack.empty()){
            _data.push(temp_stack.top());
            temp_stack.pop();
        }
    }
    void pop(){
        _data.pop();
        return;
    }
    int front(){
        return _data.top();
    }
    int back(){
        std::stack<int> temp_stack;
        while (!_data.empty()){
            temp_stack.push(_data.top());
            _data.pop();
        }
        return temp_stack.top();
    }
    bool empty(){
        return _data.empty();
    }
};
3. 包含min函数的栈(easy)(栈)

题目说明:
设计一个栈,支持push、pop、top、getMin(返回栈内最小元素 )等功能,并保证时间复杂度为常数

难点分析:栈的pop操作会导致最小元素动态变化,所以我们需要以动态规划的方式记录最小值:dp(n) = x > dp(n-1) ? dp(n-1) : x;

class MinStack {
private:
    std::stack<int> _data;
    std::stack<int> _min; //增加空间复杂度来换取时间复杂度
public:
    MinStack(){}
    void push(int x){
        _data.push(x);
        if (_min.empty()){
            _min.push(x);
        }
        else{
            if(x > min.top()){
                x = min.top();  //_data每一次元素都对应_min相应元素,代表截至该层 栈内的最小元素
            }
            _min.push(x);
        }
    }
    void pop(){
        _data.pop();
        _min.pop();
    }
    int top(){
        return _data.top();
    }
    int getMin(){
        return _min.top();
    }
    bool empty(){
        return _data.empty();
    }
};
4. 合法的出栈序列(medium)(栈、队列)

题目说明:
已知从1至n的数字序列,按顺序入栈,每个数字入栈后可选择立即出栈,也可以在栈中停留,等待后边数字入栈出栈后再出栈。

求某一数字序列是否原始数字序列按上述规则操作后的结果

举例:

[3,2,5,4,1] <-- { [1,2,3,4,5] -> 1进栈 -> 2进栈 -> 3进栈 -> 3出栈 -> 2出栈 -> 4进栈 -> 5进栈 -> 5出栈 -> 4出栈 -> 1出栈 }故合法

思路:模拟正常序列向给定序列的变化工程,将i先插入到中间栈中,对比中间栈顶部元素与给定序列顶部元素,相同则不断pop,否则,继续将元素push到中间栈,最后检查中间栈是否为空来判断是否合法

class Solution {
public:
    bool check_is_valid_order(queue<int> &order){
        stack<int> stk;
        int n = order.size();
        for(int i = 1; i <= n; i++){
            stk.push(i);
            while(!stk.empty() && stk.top() == order.front()){
                stk.pop();
                order.pop();
            }
        }
        if (!stk.empty()){
            return false;
        }
        return true;
    }
};
5. 简单的计算器(hard)(栈)

题目说明:
设计一个计算器,输入一个字符串存储的数字表达式,可以计算包括"("、")"、"+"、"-"四种符号的数字表达式,输入的数学表达式字符串保证是合法的。输入表达式中可能存在空格字符

举例:

“(1+1)” = 2

思路:状态机 + 栈

class Solution {
public:
    int calculate(string s){
        static const int STATE_BEGIN = 0;
        static const int STATE_NUMBER = 0;
        static const int STATE_OPERATION = 0;
        stack<int> number_stack;
        stack<char> operation_stack;
        int number = 0;
        int STATE = STATE_BEGIN;
        int compuate_flag = 0;
        for(int i = 0; i < s.length(); i++){
            if(s[i] == ' ') continue;
            switch(STATE){
                case STATE_BEGIN:{
                    if(s[i] >= '0' && s[i] <= '9')
                        STATE = STATE_NUMBER;
                    else
                        STATE = STATE_OPERATION;
                    i --;
                    break;
                }
                case STATE_NUMBER:{
                    if(s[i] >= '0' && s[i] <= '9')
                        number = number * 10 + s[i] - '0';
                    else{
                        number_stack.push(number);
                        if(compuate_flag)
                            compute(number_stack, operation_stack);
                        number = 0;
                        i--;
                        STATE = STATE_OPERATION;
                    }
                    break;
                }
                case STATE_OPERATION:{
                    if(s[i] == '+' || s[i] == '-'){
                        operation_stack.push(s[i]);
                        compuate_flag = 1;
                    }
                    else if(s[i] == '('){
                        STATE = STATE_NUMBER;
                        compuate_flag = 0;
                    }
                    else if(s[i] >= '0' && s[i] <= '9'){
                        STATE = STATE_NUMBER;
                        i--;
                    }
                    else if(s[i] == ')'){
                        ccompute(number_stack, operation_stack);
                    }
                    break;
                }
            }
            if (number != 0){
                number_stack.push(number);
                compute(number_stack, operation_stack);
            }
            if (number == 0 && number_stack.empty()){
                return 0;
            }
            return number_stack.top();
        }
    }
};
6. 数组中第K大的数(easy)(堆)

题目说明:
已知一个未排序的数组,求这个数组中第K大的数字

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k){
        priority_queue<int, vector<int>, greater<int>> small_heap;
        for(int i = 0; i < nums.size(); i++){
            if (i < k){
                small_heap.push(nums[i]);
            }
            else if (nums[i] > small_heap.top()){
                small_heap.pop();
                small_heap.push(nums[i]);
            }
        }
        return small_heap.top();
    }
};
7. 寻找中位数(hard)(堆)

题目说明:
设计一个数据结构,该数据结构动态维护一组数据,且支持以下操作:

添加元素

返回中位数(如果为个数为奇,返回平均值)

思路:
动态维护一个最大堆与一个最小堆,最大堆存储一半数据,最小堆存储一半数据,维持最大堆堆顶比最小堆堆顶小

class MedianFinder {
public:
    priority_queue<int, vector<int>, greater<int>> small_queue; //最小堆构造方法
    priority_queue<int, vector<int>, less<int>> big_queue; //最大堆构造方法

    MedianFinder(){
    }
    void addNum(int num){
        if (big_queue.empty() || num <= big_queue.top()){
            big_queue.push(num);
            if (big_queue.size() > small_queue.size() + 1){
                small_queue.push(big_queue.top());
                big_queue.pop();
            }
        }
        else{
            small_queue.push(num);
            if (big_queue.size() < small_queue.size()){
                big_queue.push(small_queue.top());
                small_queue.pop();
            }
        }
    }
    double findMedian(){
        if (big_queue.size() > small_queue.size())
            return big_queue.top();
        return (big_queue.top() + small_queue.top()) / 2.0; 
    }
};
  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值