LeetCode分类刷题(六):堆栈(Heap & Stack)

数据结构中的堆栈,这个就是我们大学课程《数据结构》中所学到的。通俗上的堆栈的理解,堆和栈是数据存储方式的两种数据结构。关于堆栈,其实还有一个比较容易搞混的地方那就是队列,其实这三种都是数据结构中的一种排序数据结构。 

  1. 堆:堆的数据机构其实就是一个完全二叉树,具堆属性的数据结构才可被叫做为堆,堆常见的应用就是堆排序与实现优先队列,为什么用?因为快啊 。
  2. 队列:就是先进先出的存储方式,类似与超市付款,先买的先走,一般与栈作比较 。
  3. 栈:与队列相反,栈的顺序是后进先出,只可以在栈顶进行操作,类似与只有一个出入口的公交车,先上车的只能后下车 。

堆:

  • 堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:堆中某个节点的值总是不大于或不小于其父节点的值;堆总是一棵完全二叉树。
  • 将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。

队列:

  • 队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除。进行插入的一端称为队尾(rear),插入动作称为进队或入队,进行删除的一端称为队头(front),删除动作称为出队。
  • 队列的性质:先进先出(First-in, First-out)
  • 双向队列:队列的两端都允许进行进队和出队操作

栈:

  • 栈(stack)又名堆栈,一个数据集合,可以理解为只能在一端进行插入或删除操作的列表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底
  • 栈的性质:后进先出
  • 栈就是一个桶,后放进去的先拿出来,它下面本来有的东西要等它出来之后才能出来(先进后出)

LeetCode中关于栈和堆的题目有以下三种类型题:

(一)堆栈之栈相关题目:

(二)堆栈之堆相关题目:

(三)堆栈之综合相关题目:


(一)堆栈之栈相关题目:

20. Valid Parentheses

  • Given a string containing just the characters '('')''{''}''[' and ']', determine if the input string is valid.
  • 题目要求:这道题让我们验证输入的字符串是否为括号字符串,包括大括号,中括号和小括号。
  • 题目分析:用一个栈,我们开始遍历输入字符串,如果当前字符为左半边括号时,则将其压入栈中,如果遇到右半边括号时,若此时栈为空,则直接返回false,如不为空,则取出栈顶元素,若为对应的左半边括号,则继续循环,反之返回false。
  • 题目解答:
class Solution {
public:
    bool isValid(string s) {
        stack<char> parentheses;
        for(int i = 0; i < s.size(); i++){
            if(s[i] == '(' || s[i] == '[' || s[i] == '{'){
                parentheses.push(s[i]); 
            }else{
                if(parentheses.empty()) return false;
                if(s[i] == ')' && parentheses.top() != '(') return false;
                if(s[i] == ']' && parentheses.top() != '[') return false;
                if(s[i] == '}' && parentheses.top() != '{') return false;
                parentheses.pop();
            }
        }
        return parentheses.empty();
    }
};

150. Evaluate Reverse Polish Notation

  • Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +-*/. Each operand may be an integer or another expression.
  • 题目要求:计算逆波兰表达式。逆波兰表达式就是把操作数放前面,把操作符后置的一种写法,我们通过观察可以发现,第一个出现的运算符,其前面必有两个数字,当这个运算符和之前两个数字完成运算后从原数组中删去,把得到一个新的数字插入到原来的位置,继续做相同运算,直至整个数组变为一个数字。
  • 题目分析:仔细想想,这道题果然应该是栈的完美应用啊,从前往后遍历数组,遇到数字则压入栈中,遇到符号,则把栈顶的两个数字拿出来运算,把结果再压入栈中,直到遍历完整个数组,栈顶数字即为最终答案。
  • 题目解答:
class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        if(tokens.size() == 1) return stoi(tokens[0]);
        stack<int> stk;
        for(int i = 0; i < tokens.size(); i++){
            if(tokens[i] != "+" && tokens[i] != "-" && tokens[i] != "*" && tokens[i] != "/") stk.push(stoi(tokens[i]));
            else{
                int num1 = stk.top();stk.pop();
                int num2 = stk.top();stk.pop();
                if(tokens[i] == "+") stk.push(num2 + num1);
                if(tokens[i] == "-") stk.push(num2 - num1);
                if(tokens[i] == "*") stk.push(num2 * num1);
                if(tokens[i] == "/") stk.push(num2 / num1);
            }
        }
        return stk.top();
    }
};

(二)堆栈之堆相关题目:

347. Top K Frequent Elements

  • Given a non-empty array of integers, return the k most frequent 
  • 题目要求:这道题给了我们一个数组,让我们统计前k个高频的数字。
  • 题目分析:对于这类的统计数字的问题,首先应该考虑用HashMap来做,建立数字和其出现次数的映射,然后再按照出现次数进行排序。我们可以用堆排序来做,使用一个最大堆来按照映射次数从大到小排列,在C++中使用priority_queue来实现,默认是最大堆。
  • 题目解答:
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        vector<int> res;
        unordered_map<int, int> hash;
        priority_queue<pair<int, int>> heap;
        for(auto num : nums) hash[num]++;
        for(auto it : hash) heap.push({it.second, it.first});
        for(int i = 0; i < k; i++){
            res.push_back(heap.top().second);
            heap.pop();
        }
        return res;
    }
};

506. Relative Ranks

  • Given scores of N athletes, find their relative ranks and the people with the top three highest scores, who will be awarded medals: "Gold Medal", "Silver Medal" and "Bronze Medal".
  • 题目要求:这道题给了我们一组分数,让我们求相对排名,前三名分别是金银铜牌,后面的就是名次数。
  • 题目分析:利用堆来排序,建立一个优先队列,把分数和其坐标位置放入队列中,会自动按其分数高低排序,然后我们从顶端开始一个一个取出数据,由于保存了其在原数组的位置,我们可以直接将其存到结果res中正确的位置,用一个变量cnt来记录名词,前三名给奖牌,后面就是名次数。
  • 题目解答:
class Solution {
public:
    vector<string> findRelativeRanks(vector<int>& nums) {
        int n = nums.size(), cnt = 1;
        vector<string> res(n, "");
        priority_queue<pair<int, int>> heap;
        for(int i =0; i <n; i++){
            heap.push({nums[i], i});
        }
        for(int i = 0; i < n; i++){
            int dx = heap.top().second;
            heap.pop();
            if(cnt == 1) res[dx] = "Gold Medal";
            else if(cnt == 2) res[dx] = "Silver Medal";
            else if(cnt == 3) res[dx] = "Bronze Medal";
            else res[dx] = to_string(cnt);
            cnt++;
        }
        return res;
    }
};

373. Find K Pairs with Smallest Sums

  • You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. Define a pair (u,v) which consists of one element from the first array and one element from the second array. Find the k pairs (u1,v1),(u2,v2) ...(uk,vk) with the smallest sums.
  • 题目要求:这道题给了我们两个数组,让我们从每个数组中任意取出一个数字来组成不同的数字对,返回前K个和最小的数字对。
  • 题目分析:先来看brute force的解法,这种方法我们从0循环到数组的个数和k之间的较小值,这样做的好处是如果k远小于数组个数时,我们不需要计算所有的数字对,而是最多计算k*k个数字对,用了priority_queue,也需要我们自定义比较器。
  • 题目解答:
class Solution {
public:
    vector<pair<int, int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        vector<pair<int, int>> res;
        priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> heap;
        for (int i = 0; i < min((int)nums1.size(), k); ++i) {
            for (int j = 0; j < min((int)nums2.size(), k); ++j) {
                if (heap.size() < k) {
                    heap.push({nums1[i], nums2[j]});
                } else if (nums1[i] + nums2[j] < heap.top().first + heap.top().second) {
                    heap.push({nums1[i], nums2[j]}); heap.pop();
                }
            }
        }
        while (!heap.empty()) {
            res.push_back(heap.top()); heap.pop();
        }
        return res;
    }
    struct cmp {
        bool operator() (pair<int, int> &a, pair<int, int> &b) {
            return a.first + a.second < b.first + b.second;
        }
    };
};

(三)堆栈之综合相关题目:

155. Min Stack

  • Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
  • 题目要求:这道最小栈跟原来的栈相比就是多了一个功能,可以返回该栈的最小值。
  • 题目分析:使用两个栈来实现,一个栈来按顺序存储push进来的数据,另一个用来存出现过的最小值。
  • 题目解答:
class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
        
    }
    
    void push(int x) {
        if(s2.empty() || x <= s2.top()) s2.push(x);
        s1.push(x);
    }
    
    void pop() {
        if(s1.top() == s2.top()) s2.pop();
        s1.pop();
    }
    
    int top() {
        return s1.top();
    }
    
    int getMin() {
        return s2.top();
    }
    stack<int> s1, s2;
};

225. Implement Stack using Queues

  • Implement the following operations of a stack using queues.
  • 题目要求:这道题让我们用队列来实现栈,队列和栈作为两种很重要的数据结构,它们最显著的区别就是,队列是先进先出,而栈是先进后出。
  • 题目分析:这种方法的原理就是每次把新加入的数插到前头,这样队列保存的顺序和栈的顺序是相反的,它们的取出方式也是反的,那么反反得正,就是我们需要的顺序了。我们需要一个辅助队列tmp,把s的元素也逆着顺序存入que中,此时加入新元素x,再把que中的元素存回来,这样就是我们要的顺序了,其他三个操作也就直接调用队列的操作即可。
  • 题目解答:
class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() {
        
    }
    
    /** Push element x onto stack. */
    void push(int x) {
        que.push(x);
        for(int i = 0; i < que.size() - 1; i++){
            que.push(que.front());
            que.pop();
        }
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int temp = que.front();
        que.pop();
        return temp;
    }
    
    /** Get the top element. */
    int top() {
        return que.front();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return que.empty();
    }
    queue<int> que;
};

232. Implement Queue using Stacks

  • Implement the following operations of a queue using stacks.
  • 题目要求:这道题让我们用栈来实现队列。
  • 题目分析:使用了两个栈input和output,其中新进栈的都先缓存在input中,入股要pop和peek的时候,才将input中所有元素移到output中操作,提高了效率。
  • 题目解答:
class MyQueue {
public:
    /** Initialize your data structure here. */
    MyQueue() {
        
    }
    
    /** Push element x to the back of queue. */
    void push(int x) {
        input.push(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() {
        int temp = peek();
        output.pop();
        return temp;
    }
    
    /** Get the front element. */
    int peek() {
        if(output.empty()){
            while(input.size()){
                output.push(input.top());
                input.pop();
            }
        }
        return output.top();
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {
        return input.empty() &&output.empty();
    }
    stack<int> input, output;
};

如果各位看官们,大神们发现了任何错误,或是代码无法通过OJ,或是有更好的解法,或是有任何疑问,意见和建议的话,请一定要在帖子下面评论区留言告知博主啊,多谢多谢,祝大家刷得愉快,刷得精彩,刷出美好未来~

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值