目录
1.若进栈序列为1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈系列是()
栈继承父类Vector里有Object[] 数组,栈的底层是用数组实现的。
概念
![](https://i-blog.csdnimg.cn/blog_migrate/7f4ff218c371ccf53492911d68b96949.png)
栈的方法
push():入栈
peek():获取栈顶元素,但是不删除(偷偷瞄一眼)
pop():出栈,删除栈顶元素
栈的模拟实现
基于数组实现栈定义类
public class MyStack {
//数组
private int [] elem;
//定义usedSize记录栈的元素个数,方便出栈入栈。
private int usedSize;
private static final int DEFAULT_CAPACITY = 10;
private MyStack() {
this.elem = new int[DEFAULT_CAPACITY];
}
}
数组是从零开始的
usedSize和elem数组下标差值为1!!!
在实现pop()方法时,我们直接通过usedSize--即可
例如下图我们要出栈顶元素56,此时usedSize值为 3 ,让usedSize-1
即可达到出栈效果,在下一次新的元素入栈时可以把 usedSize(2) 这里的元素替代掉
import java.util.Arrays;
public class MyStack {
private int [] elem;
private int usedSize;
private static final int DEFAULT_CAPACITY = 10;
private MyStack() {
this.elem = new int[DEFAULT_CAPACITY];
}
public void push(int val) {
if(isFull()) {
//如果,满了,扩容
this.elem = Arrays.copyOf(elem,usedSize*2);
}
this.elem[usedSize] = val;
this.usedSize++;
}
//判断是否为满
public boolean isFull() {
return this.usedSize==this.elem.length;
}
//不是真正的出栈 因为通过usedSize入栈
// usedSide--
// 后面进入栈是通过usedSize 操作的,在usedSide--后,入栈的新元素,会把,栈顶元素替代掉
// 相当于 让栈顶元素出栈
public int pop(int val) {
if(isEmpty()) {
throw new EmptyException("栈为空");
}
//拿出要出栈的元素
int ret = this.elem[this.usedSize-1];
//更新实际存在元素
this.usedSize--;
return ret;
}
public int peek() {
if(isEmpty()) {
throw new EmptyException("栈为空");
}
return elem[usedSize-1];
}
public boolean isEmpty() {
return this.usedSize == 0;
}
}
//自定义异常:
public class EmptyException extends RuntimeException{
public EmptyException() {
}
public EmptyException(String message) {
System.out.println(message);
}
}
栈的模拟实现不难,更具有挑战性的是栈的笔试题,编程题。
栈面试题
1.若进栈序列为1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈系列是()
A.1,2,3,4 B. 2,3,4,1 C. 3,1,4,2 D. 3,4,2,1
画图带入演示即可:
例如A项 (题中说明进栈过程可以出栈)
1.1进栈,1出栈
2.2进栈,2出栈
后面同理,所以出栈序列可以是1,2,3,4
2.打印列表:
逆序打印:
递归方式:
void printList(Node head) {
if(null!=head) {
printList(head.next);
System.out.print(head.val+"");
}
}
在递归调用printList方法是系统申请了栈:
第一次四次进入printList时,head.next为空,开始打印45,以此一直返回带最开始
类似于最后进栈的45最先出的的栈的数据结构(在栈上开辟栈)
栈的方式逆转打印
//逆转打印链表
public void reversePrintList() {
Stack<ListNode> stack = new Stack<>();
ListNode cur = head;
//先入栈
while (cur!=null) {
stack.push(cur);
cur = cur.next;
}
//出栈就为逆序了
while (stack.isEmpty()) {
ListNode top = stack.pop();
System.out.println(stack.pop().val);
}
}
3.括号匹配
题目链接:
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
例子:
思路:
带入示例遍历可以总结:第一个遇到的右括号,要和最后一个左括号匹配
所以我们可以借助栈这种数据结构,在遍历整个括号字符串时,遇到左边的括号,往栈里存。
在栈不为空的前提下,遇到右括号时出栈。
当遍历完全部时,栈不为空,说明栈里还有为和其匹配的括号。
代码实现:
class Solution {
public static boolean isValid(String s) {
Stack<Character>stack = new Stack<>();
for(int i = 0;i < s.length();i++) {
char ch = s.charAt(i);
//左括号,入栈
if(ch == '('||ch == '{'||ch == '[') {
stack.push(ch);
}else{
// else 里的情况 说明是右括号,栈为空,说明栈里没有和右括号匹配的
if(stack.empty()) {
return false;
}
// top 拿的是栈里左括号 ch 拿的是右括号
char top = stack.peek();
if(top == '(' && ch==')'||top == '{' && ch=='}'||top == '[' && ch==']') {
stack.pop();
}else{
return false;
}
}
}
if(!stack.empty()) {
return false;
}
return true;
}
}
4.逆波兰表达式
题目链接:
给你一个字符串数组 tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。 - 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
思路:
中缀表达式:(1+2)*5 平时我们最常见的)
后缀表达式:1 2 + 5 *
中缀表达式变后缀表达式的方法:
第一步:先乘除后加减的添加括号
例如 (1+2)*5 先对向 * 的两个整体加上括号 ((1+2)*5 ) 因为 1+2 已经有括号了,就不需要加了
第二步:把运算符(+-*/) 往离它最近的括号后(右)移
(如果要变换为前缀表达式,就往离它最近的括号前(左) ——》((12)+5 )*
第三步: 把全部括号清除
((12)+5 )* ——》 12+5*
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for(String s :tokens) {
if(isNum(s)) {
//这里拿到的是字符串 通过Integer.parseInt(s) 可以转为int
stack.push(Integer.parseInt(s));
}else{
//出栈的第一个数字为右操作数
int num2 = stack.pop();
int num1 = stack.pop();
switch(s)
{
case "+":
stack.push(num1 + num2);
break;
case "-":
stack.push(num1 - num2);
break;
case "*":
stack.push(num1 * num2);
break;
case "/":
stack.push(num1 / num2);
break;
}
}
}
//最后栈底为最终的结果
return stack.pop();
}
public boolean isNum(String s) {
if((s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/")) {
return false;
}
return true;
}
}
5. 栈的压入、弹出序列
题目链接:
题目描述:
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
1. 0<=pushV.length == popV.length <=1000
2. -1000<=pushV[i]<=1000
3. pushV 的所有数字均不相同
示例:
示例1
输入:
[1,2,3,4,5],[4,5,3,2,1]返回值:
true
说明:
可以通过push(1)=>push(2)=>push(3)=>push(4)=>pop()=>push(5)=>pop()=>pop()=>pop()=>pop() 这样的顺序得到[4,5,3,2,1]这个序列,返回true
示例2
输入:
[1,2,3,4,5],[4,3,5,1,2]返回值:
false
说明:
由于是[1,2,3,4,5]的压入顺序,[4,3,5,1,2]的弹出顺序,要求4,3,5必须在1,2前压入,且1,2不能弹出,但是这样压入的顺序,1又不能在2之前弹出,所以无法形成的,返回false
思路:此题的选择题形式和上述的第一题一样,我们需通过代码判断
代码实现:
import java.util.*;
public class Solution {
public boolean IsPopOrder (int[] pushV, int[] popV) {
Stack<Integer> stack = new Stack<>();
int j = 0;
// 这里第一次敲,把j放for循环里定义了,导致部分不通过
for(int i = 0; i < pushV.length;i++) {
stack.push(pushV[i]);
while(!stack.empty()&&j<popV.length
&&stack.peek()==popV[j]) {
stack.pop();
j++;
}
}
return stack.empty();
}
}
6.最小栈
题目链接:
设计一个支持 push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack
类:
MinStack()
初始化堆栈对象。void push(int val)
将元素val推入堆栈。void pop()
删除堆栈顶部的元素。int top()
获取堆栈顶部的元素。int getMin()
获取堆栈中的最小元素。
示例:
示例 1:
输入: ["MinStack","push","push","push","getMin","pop","top","getMin"] [[],[-2],[0],[-3],[],[],[],[]] 输出: [null,null,null,null,-3,null,0,-2] 解释: MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); --> 返回 -3. minStack.pop(); minStack.top(); --> 返回 0. minStack.getMin(); --> 返回 -2.
提示:
-231 <= val <= 231 - 1
pop
、top
和getMin
操作总是在 非空栈 上调用push
,pop
,top
, andgetMin
最多被调用3 * 104
次
思路:
入栈:push()
题中提到在常数时间内检索到最小元素的栈,即时间复杂度为O(1)。
我们可以考虑使用两个栈
一个正常输入,另外一个存最小值
代码实现:
import java.util.Stack;
class MinStack {
private Stack<Integer> stack;
private Stack<Integer> minStack;
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack.push(val);
if(minStack.empty()) {
minStack.push(val);
}else{
if(val<=minStack.peek()) {
minStack.push(val);
}
}
}
public void pop() {
if(!stack.empty()) {
int ret = stack.pop();
if(minStack.peek()==ret) {
minStack.pop();
}
}
}
public int top() {
if(stack.empty()) {
return -1;
}
return stack.peek();
}
public int getMin() {
if(minStack.empty()) {
return -1;
}
return minStack.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/