数据结构中的堆栈,这个就是我们大学课程《数据结构》中所学到的。通俗上的堆栈的理解,堆和栈是数据存储方式的两种数据结构。关于堆栈,其实还有一个比较容易搞混的地方那就是队列,其实这三种都是数据结构中的一种排序数据结构。
- 堆:堆的数据机构其实就是一个完全二叉树,具堆属性的数据结构才可被叫做为堆,堆常见的应用就是堆排序与实现优先队列,为什么用?因为快啊 。
- 队列:就是先进先出的存储方式,类似与超市付款,先买的先走,一般与栈作比较 。
- 栈:与队列相反,栈的顺序是后进先出,只可以在栈顶进行操作,类似与只有一个出入口的公交车,先上车的只能后下车 。
堆:
- 堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:堆中某个节点的值总是不大于或不小于其父节点的值;堆总是一棵完全二叉树。
- 将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。
队列:
- 队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除。进行插入的一端称为队尾(rear),插入动作称为进队或入队,进行删除的一端称为队头(front),删除动作称为出队。
- 队列的性质:先进先出(First-in, First-out)
- 双向队列:队列的两端都允许进行进队和出队操作
栈:
- 栈(stack)又名堆栈,一个数据集合,可以理解为只能在一端进行插入或删除操作的列表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。
- 栈的性质:后进先出
- 栈就是一个桶,后放进去的先拿出来,它下面本来有的东西要等它出来之后才能出来(先进后出)
LeetCode中关于栈和堆的题目有以下三种类型题:
(一)堆栈之栈相关题目:
- 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();
}
};
(二)堆栈之堆相关题目:
- 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;
}
};
- 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;
}
};
};
(三)堆栈之综合相关题目:
- 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,或是有更好的解法,或是有任何疑问,意见和建议的话,请一定要在帖子下面评论区留言告知博主啊,多谢多谢,祝大家刷得愉快,刷得精彩,刷出美好未来~