这里的栈与我们之前常说的栈是不同的。之前我们说的栈是内存栈,它是JVM内存的一部分,用于存储局部变量、方法调用信息等。每个线程都有自己独立的栈空间,当线程启动时,栈就会被创建;线程结束,栈也会被销毁。
而数据结构中的栈是一种抽象数据类型,描述的是一种存储数据的一种方法,遵循“先进后出”的原则,是一种线性的数据结构。
像上图所示就是一个栈,只能对于顶部完成操作,放元素放在最上面,当要拿栈中的元素也只能从最上面的元素开始获取。
方法 | 功能 |
---|---|
Stack() | 构造方法 |
E push(E e) | 将e入栈 |
E pop() | 将栈顶元素出栈并返回 |
E peek() | 获取栈顶元素 |
int size | 获取栈中的有效元素个数 |
boolean empty() | 栈中是否为空 |
int search(Object o) |
public static void test1() {
Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.push(20);
stack.push(30);
stack.push(40);
stack.pop();
System.out.println(stack);//[10, 20, 30]
System.out.println(stack.size());//3
System.out.println(stack.peek());//30
System.out.println(stack.empty());//false
System.out.println(stack.search(10));//3
}
1、对于search方法的返回:如果对象存在于栈中,会返回该对象到栈顶的距离(栈顶元素的距离为 1);若对象不在栈中,则返回 -1。
2、由于Stack继承了Vector等其他类,也可以调用Vecto等其他类中的方法。
import java.util.Arrays;
public class MyStack<E> {
private Object[] elem;
private int useSize;
public static final int DEFAULT_CAPACITY = 10;
public MyStack() {
elem = new Object[DEFAULT_CAPACITY];
}
/**
* 完成入栈操作
*/
public void push(E data) {
if(isFull()) {
elem = Arrays.copyOf(elem,elem.length*2);
}
elem[useSize] = data;
useSize++;
}
/**
* 完成出栈操作,将栈顶的元素出栈并返回
*/
public E pop() {
if(isEmpty()) {
return null;
}
E ret = (E)elem[useSize - 1];
useSize--;
return ret;
}
/**
* 返回栈顶的元素
*/
public E peek() {
if(elem == null) {
return null;
}
return (E)elem[useSize - 1];
}
/**
* 栈中的元素个数
* @return
*/
public int size() {
return useSize;
}
private boolean isFull() {
return elem.length == useSize;
}
public boolean isEmpty() {
return useSize == 0;
}
public void display() {
for (int i = 0; i < useSize; i++) {
System.out.print(elem[i] + " ");
}
System.out.println();
}
}
1、定义的 MyStack 类属于泛型类,也就是 MyStack
2、Stack类底层使用数组实现的,当然我们也可以用链表实现Stack类。
3、 我们也可以使用链表的形式实现栈,在LinkedList类中也有push、pop、peek方法等方法。
对于之前实现是通过递归的方法
public void printList(ListNode head) {
ListNode cur = head;
if(head != null) {
printList(cur.next);
System.out.print(cur.value + " ");
}
}
通过递归回代的机制实现链表的逆序打印。
这也可以看成先进后出的。正序是从头开始打印,先将元素放在栈中,在开始取出元素,取出的元素对于链表来说就是逆序输出。
public void print(ListNode head) {
ListNode cur = head;
if(head == null) {
return;
}
Stack<ListNode> stack = new Stack<>();
//从头节点开始依次存入栈中
while (cur != null) {
stack.push(cur);
cur = cur.next;
}
//开始取出元素,此时取出的是链表中最后的节点,因为它是最后放入栈中的
while (!stack.empty()) {
System.out.print(stack.pop().value + " ");
}
System.out.println();
}
逆波兰表达式也叫后缀表达式。我们平常算数用的是中缀表达式(例如:1 + 2 = 3)。关于后缀表达式怎么从中缀表达式得来的,可以自行百度。下面是豆包给出来的运算方法。
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for(String string : tokens) {
if(!isOperations(string)) {
//是数字将其存入栈中
stack.push(Integer.parseInt(string));
}else {
//是操作符,取出两个数字进行计算,将运算的数字在存入栈中
int right = stack.pop();
int left = stack.pop();
switch (string){
case "+" :
stack.push(left + right);
break;
case "-" :
stack.push(left - right);
break;
case "*" :
stack.push(left * right);
break;
case "/" :
stack.push(left / right);
break;
}
}
}
//返回栈中最后一个元素了
return stack.pop();
}
private boolean isOperations(String string) {
return string.equals("+") || string.equals("-") || string.equals("*") || string.equals("/");
}
public boolean IsPopOrder (int[] pushV, int[] popV) {
Stack<Integer> stack = new Stack<>();
int j = 0;
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();
}
for循环是将pushV数组中的元素依次放在栈中。while循环是按照popV数组取出来的,当栈顶上的元素等于popV中的第一个元素,就从栈中取出,popV往后走。为什么判断条件要有!stack.empty()
?是因为防止stack.peek()为空指针异常。
class MinStack {
Stack<Integer> stack;
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() {
int popVal = stack.pop();
if(popVal == minStack.peek()) {
minStack.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
1、在push()方法中将小的之push到minStack栈中。此时需要注意的是,当有两个紧挨一样的最小值,它们都需要push到minStack栈中,因为当stack栈中pop了这个值,但最小值还是它。
2、在pop方法中if语句的判断条件需要注意。由于栈中存储的是Integer类对象,比较时不能直接用等号(stack.pop() == minStack.peek()
像这样是错的)。可以定义int类型的临时变量,在比较的时候Integer类型的会自动拆箱。(在push方法中if语句比较也是一样的)