猿六今天复习完栈的相关知识,打算玩会游戏放松一下,突然接到面试官的电话,急忙跑到厕所接听。
面试官:猿同学,你简历上说熟悉数据结构,说说你对栈的理解。
猿六:先进后出,后进先出,就像羽毛球桶一样,桶口的羽毛球先被用,里面的羽毛球后被用。
面试官:你来用数组实现下栈,功能有:进栈,出栈,求栈顶元素,判断栈是否为空。
面试官:假设最多做10W次进栈出栈操作,所有操作合法。
猿六:具体过程如下:
1. 初始化:数组stk保存元素,从stk[1]开始存储。top指向栈顶元素,栈为空的时候,top = 0.
2. 判断栈是否为空:top等于0栈空,top 大于 0 栈非空。
3. 入栈:top 增大 1,stk[top] 中放入入栈元素。
4. 出栈:top 减小1。
5. 求栈顶元素:返回stk[top]
代码如下:
private:
int stk[100010];
int top = 0;
public:
void push(int x)//top+1后放入x
{
stk[++top] = x;
}
int quary()//返回top处存储的值:stk[top]
{
return stk[top];
}
void pop()//top--即可
{
top--;
}
bool isEmpty()//空返货0,非空返回1
{
return top == 0 ? 0 : 1;
}
};
面试官:说几个算法中用的栈的情况。
猿六:直接用到栈性质的有括号匹配问题。
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
算法原理:
是否是有效括号不能通过计算左右括号的数量进行判断。
假如输入: [{]},每种括号的左右数量分别相等,但不是有效的括号。因此有效括号不仅要求每种括号的左右数量相同,还与括号位置有关。
可以这样做:对给定的字符串 s 进行遍历,当遇到一个左括号时,希望在后续的遍历中,有一个相同类型的右括号与其配对。由于后遇到的左括号要先闭合,符合后进先出的原则,可以用栈来进行处理。
当遇到一个右括号时,需要一个相同类型的左括号闭合。取出栈顶的左括号,判断它们是否是相同类型的括号。如果不是相同的类型,或者栈中并没有左括号,则字符串 s 无效,返回 false。如果遍历完字符串s后,栈为空,则字符串 s 合法,返回 true。
以:“({}[])” 为例,过程如下:
代码如下:
class Solution {
public:
bool Ismatch(const char& rhs,const char& lhs)//判断是否匹配
{
switch(lhs)
{
case '(':
return rhs==')';
case '[':
return rhs==']';
case '{':
return rhs=='}';
default:
return false;
}
}
bool isValid(string s) {
if(s.size() % 2 != 0) return 0; //字符串长度不是偶数,不合法
char stk[100010];
int top = 0;
for(auto c : s)
{
if(c == '(' || c == '[' || c == '{')//做符号进栈
stk[++top] = c;
else if(top && Ismatch(c, stk[top]))//右符号与栈顶匹配,栈顶出栈
top--;
else
return false;
}
if(!top)//栈空,字符串合法
return true;
return false;
}
};
面试官:单调栈你了解吗,讲讲对单调栈的理解和具体使用场景。
猿六:单调栈的性质如下:
- 单调栈里面的元素具有单调性;
- 元素加入栈前会把栈顶破坏单调性的元素删除;
- 使用单调栈可以找到元素向左遍历的第一个比他小的元素(单增栈),也可以找到元素向左遍历第一个比他大的元素(单减栈);
一般使用单调栈的题目具有以下的两点:
- 离自己最近(栈的后进先出的性质)
- 比自己大(小)、高(低);
模拟一下单调递增栈:
有一个组数3,10,7,4,12。从左到右依次入栈,则如果栈为空或入栈元素值大于栈顶元素值,则入栈;否则,如果入栈则会破坏栈的单调性,则需要把比入栈元素大的元素全部出栈后,元素再入栈。
- 3入栈时,栈为空,直接入栈,栈内元素为3。 10入栈时,栈顶元素3比10小,入栈,栈内元素为 3,10。
- 7入栈时,栈顶元素10比7大,栈顶元素出栈,此时栈顶元素为 3,比7小。7入栈,栈内元素为 3,7。
- 4入栈时,栈顶元素7比4大,则栈顶元素出栈,此时栈顶元素为 3,比4小。4入栈,栈内元素为 3,4。
- 12入栈时,栈顶元素4比12小,12入栈,栈内元素为 3,4,12。
模板代码:
//cpp
//作者:yxc
stack<int> stk;
for (遍历这个数组)
{
if (栈空 || 栈顶元素大于等于当前比较元素)
{
入栈;
}
else
{
while (栈不为空 && 栈顶元素小于当前元素)
{
栈顶元素出栈;
更新结果;
}
当前数据入栈;
}
}
面试官:说说具体可以用在什么地方。
猿六:例如这道题,可以用单调栈解决。
给定一个长度为N的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出-1。
输入格式
第一行包含整数N,表示数列长度。 第二行包含N个整数,表示整数数列。
输出格式 共一行,包含N个整数,其中第i个数表示第i个数的左边第一个比它小的数,如果不存在则输出-1。
算法原理:
用单调递增栈,当该元素可以入栈的时候,栈顶元素就是它左侧第一个比它小的元素。
以:3 4 2 7 5 为例,过程如下:
代码如下:
//cpp
//作者:yxc
#include <iostream>
using namespace std;
const int N = 100010;
int stk[N], tt;
int main()
{
int n;
cin >> n;
while (n -- )
{
int x;
scanf("%d", &x);
while (tt && stk[tt] >= x) tt -- ;
if (!tt) printf("-1 ");
else printf("%d ", stk[tt]);
stk[ ++ tt] = x;
}
return 0;
}
面试官:再说说栈还能用到什么地方。
猿六 :下面这些问题的题解中,都用到了栈。
Leetcode 42:接雨水
Leetcode 496:下一个更大元素 I
Leetcode 503:下一个更大元素 II
Leetcode 739:每日温度
Leetcode 94. 二叉树的中序遍历
Leetcode144. 二叉树的前序遍历
面试官:恩,今天先到这,等下一轮面试吧。
猿六: 恩恩,谢谢黑脸面试官。
面试官:你说谁脸黑,你没下一次面试了。
我是猿六,喜欢讲故事的程序员
准备了halfrost大神的孔雀书以及经典巨著算法导论。公众号后台点击电子书,直接获取
兄哋,栈图有点不对劲。
面试官:你说谁脸黑,你没下一次面试了。