概述
计算机科学中,stack是一种线性的数据结构,只能在其一段添加和移除数据.
习惯来说,这一端称之为栈顶,另一端不能操作数据的称之为栈底,就如同生活中的一摞书
先提供一个接口:
java
public interface Stack <E>{
/**
* 向栈顶压入元素
* @param value -- 待压入值
* @returns:压入成功返回true,否则返回false
*
*/
boolean push(E value);
/**
* 从栈顶弹出元素
* @Returns:栈非空返回栈顶元素,栈为空返回null
*
*/
E pop();
/**
* 返回栈顶元素,不弹出
* @Returns:栈非空返回栈顶元素,栈为空返回null
*/
E peek();
/**
* 判断栈是否为空
* @Returns:空返回true,否则返回false
*
*/
boolean isEmpty();
/**
* 判断栈已满
* @Returns:满返回true,否则返回false
*
*/
boolean isFull();
}
链表实现:
java
import java.util.Iterator;
public class LinkedListStack <E> implements Stack<E>,Iterable<E>{
private int capacity;
private int size;
private Node<E>head = new Node<>(null,null);
public LinkedListStack(int capacity){
this.capacity = capacity;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
Node<E> p = head.next;
@Override
public boolean hasNext() {
return p!=null;
}
@Override
public E next() {
E value = p.value;
p=p.next;
return value;
}
};
}
/*
2->
head -> 2 -> 1 ->null
*/
@Override
public boolean push(E value) {
if(isFull()){
return false;
}
head.next = new Node<>(value,head.next);
size++;
return true;
}
/*
head -> 2 ->1 ->null
*/
@Override
public E pop() {
if(isEmpty()){
return null;
}
Node<E> first = head.next;
head.next = first.next;
size--;
return first.value;
}
@Override
public E peek() {
if(isEmpty()){
return null;
}
return head.next.value;
}
@Override
public boolean isEmpty() {
return head.next ==null;//也可以size==0
}
@Override
public boolean isFull() {
return size==capacity;
}
//单向链表实现
static class Node<E> {
E value;
Node<E>next;
public Node(E value,Node<E>next){
this.value = value;
this.next = next;
}
}
}
数组实现:
java
import java.util.Iterator;
public class ArrayStack<E> implements Stack<E>,Iterable<E> {
private E[] array;
private int top;//栈顶指针
/*
底 顶
0 1 2 3
a b c d
t
数组实现跟链表实现的顶部是相反的
为什么链表的栈顶在首节点?因为增删查改方便,如果放在链表尾部那每次增删查改都需要遍历
为什么数组实现的栈顶在尾部,因为数组是顺序结构
*/
@SuppressWarnings("all")
public ArrayStack(int capacity) {
this.array = (E[])new Object[capacity];
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
int p = top;
@Override
public boolean hasNext() {
return p>0;
}
@Override
public E next() {
E value = array[p - 1];
p--;
//array[--p];
return value;
}
};
}
@Override
public boolean push(E value) {
if(isFull()) {
return false;
}
array[top] = value;
top++;
//array[top++] = value;
return true;
}
@Override
public E pop() {
if(isEmpty()){
return null;
}
E value = array[top-1];
top --;
//array[--top];
return value;
}
@Override
public E peek() {
if(isEmpty()){
return null;
}
return array[top-1];
}
@Override
public boolean isEmpty() {
return top == 0;
}
@Override
public boolean isFull() {
return top == array.length;
}
}
应用
模拟如下方法调用:
java
public class Main {
public static void main(String[] args) {
System.out.println("main1");
System.out.println("main2");
method1();
method2();
System.out.println("main3");
}
public static void method1() {
System.out.println("method1");
method3();
}
public static void method2() {
System.out.println("method2");
}
public static void method3() {
System.out.println("method3");
}
}
/**
* main1
* main2
* method1
* method3
* method2
* main3
*/
模拟实现:
java
public class CPU {
static class Frame {
int exit;
public Frame(int exit) {
this.exit = exit;
}
}
static int pc = 1; // 模拟程序计数器 Program counter
static ArrayStack<Frame> stack = new ArrayStack<>(100); // 模拟方法调用栈
public static void main(String[] args) {
stack.push(new Frame(-1));
while (!stack.isEmpty()) {
switch (pc) {
case 1 -> {
System.out.println("main1");
pc++;
}
case 2 -> {
System.out.println("main2");
pc++;
}
case 3 -> {
stack.push(new Frame(pc + 1));
pc = 100;
}
case 4 -> {
stack.push(new Frame(pc + 1));
pc = 200;
}
case 5 -> {
System.out.println("main3");
pc = stack.pop().exit;
}
case 100 -> {
System.out.println("method1");
stack.push(new Frame(pc + 1));
pc = 300;
}
case 101 -> {
pc = stack.pop().exit;
}
case 200 -> {
System.out.println("method2");
pc = stack.pop().exit;
}
case 300 -> {
System.out.println("method3");
pc = stack.pop().exit;
}
}
}
}
}
/**
* main1
* main2
* method1
* method3
* method2
* main3
*/
练习一下:
E01. 有效的括号-Leetcode 20
一个字符串中可能出现 []
()
和 {}
三种括号,判断该括号是否有效
有效的例子
()[]{}
([{}])
()
无效的例子
[)
([)]
([]
思路
-
遇到左括号, 把要配对的右括号放入栈顶
-
遇到右括号, 若此时栈为空, 返回 false,否则把它与栈顶元素对比
-
若相等, 栈顶元素弹出, 继续对比下一组
-
若不等, 无效括号直接返回 false
-
-
循环结束
-
若栈为空, 表示所有括号都配上对, 返回 true
-
若栈不为空, 表示右没配对的括号, 应返回 false
-
答案(用到了上面案例中的 ArrayStack 类)
java
public boolean isValid(String s) {
ArrayStack<Character> stack = new ArrayStack<>(s.length() / 2 + 1);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '(') {
stack.push(')');
} else if (c == '[') {
stack.push(']');
} else if (c == '{') {
stack.push('}');
} else {
if (!stack.isEmpty() && stack.peek() == c) {
stack.pop();
} else {
return false;
}
}
}
return stack.isEmpty();
}
java
class Solution {
public boolean isValid(String s) {
if(s.isEmpty())
return true;
Stack<Character> stack=new Stack<Character>();
for(char c:s.toCharArray()){
if(c=='(')
stack.push(')');
else if(c=='{')
stack.push('}');
else if(c=='[')
stack.push(']');
else if(stack.empty()||c!=stack.pop())
return false;
}
if(stack.empty())
return true;
return false;
}
}
cpp
class Solution {
public:
bool isValid(string s) {
if(s.size() == 0){
return 0;
}
stack<char> st;
for(auto &ch: s){
if(ch == '(' || ch == '{' || ch == '['){
st.push(ch);//入栈
}else{
if(!st.empty()){
if(ch == ')'){
if(st.top() != '('){
return false;
}
st.pop();
}else if(ch == '}'){
if(st.top() != '{'){
return false;
}
st.pop();
}else if(ch == ']'){
if(st.top() != '['){
return false;
}
st.pop();
}
}else{
return false;
}
}
}
return st.empty();
}
};
E02-后缀表达式求值
LCR 036. 逆波兰表达式求值 - 力扣(LeetCode)
java
import java.util.LinkedList;
public class Main {
/*
逆波兰表达式也称为后缀表达式,即把运算符写在后面
从左向右进行计算
不必考虑运算优先级,即不用包含括号
/ /
/ /
/ /
/ /
/ /
/ 9 /
------
"2" 1 + 3 + *
1+2 中缀表达式
1 2 + 后缀表达式
LinkedList里面也实现了栈
*/
public int evalRPN(String[] tokens) {
LinkedList<Integer> numbers = new LinkedList<>();
for (String t : tokens) {
switch (t) {
case "+" -> {
Integer b = numbers.pop();
Integer a = numbers.pop();
numbers.push(a + b);
}
case "-" -> {
Integer b = numbers.pop();
Integer a = numbers.pop();
numbers.push(a - b);
}
case "*" -> {
Integer b = numbers.pop();
Integer a = numbers.pop();
numbers.push(a * b);
}
case "/" -> {
Integer b = numbers.pop();
Integer a = numbers.pop();
numbers.push(a / b);
}
default -> numbers.push(Integer.parseInt(t));
}
}
return numbers.pop();
}
}
E-03中缀表达式转后缀
反编译
c = a + b
看不懂也没有关系,反正编译器会讲中缀表达式转换为后缀表达式
java
import java.util.LinkedList;
/**
* 中缀表达式转后缀
*/
public class E03InfixToSuffix {
// public static void test(){
// int a = 1;
// int b =2;
// int c = a+ b;
// //在编译成class文件的时候就是把中缀表达式转换为了后缀表达式
// //先让程序运行一次,目的是让程序生成字节码文件
// }
/*
思路
这是一个栈
/ /
/ /
/ /
/ /
----
a+b ab+
a+b-c ab+c-
a+b*c abc*+
(a+b)*c ab+c*
(a+b*c-d)*e abc*+d-e*
a*(b+c) abc+*
1.遇到非运算符 直接拼串
2.遇到 + - * /
- 它的优先级比栈顶运算符高,入栈
- 否则把栈里优先级>=它 的都出栈, 它再入栈
3.遍历完成,栈里剩余运算符依次出栈
4.带()
- 左括号直接入栈,左括号优先设置为0
- 右括号就把栈里到左括号为止的所有运算符出栈
*/
public static void main(String[] args) {
System.out.println(infixToSuffix("a+b"));
System.out.println(infixToSuffix("a+b-c"));
System.out.println(infixToSuffix("a+b*c"));
System.out.println(infixToSuffix("a*b-c"));
System.out.println(infixToSuffix("(a+b)*c"));
System.out.println(infixToSuffix("a*(b+c)"));
System.out.println(infixToSuffix("a+b*c+(d*e*f)*g"));
}
/**
* 计算运算符优先级
* @param c
* @return
*/
static int priority(char c){
return switch (c) {
case '*','/'->2;
case '+','-'->1;
case '(' ->0;
default->throw new IllegalArgumentException("不合法的运算符:"+c);
};
}
static String infixToSuffix(String exp){
LinkedList<Character>stack = new LinkedList<>();//栈
StringBuilder sb = new StringBuilder(exp.length());//拼接
for (int i = 0; i < exp.length(); i++) {
char c = exp.charAt(i);
switch (c) {
case '+' ,'-','*','/'->{
if(stack.isEmpty()){
stack.push(c);
}else{
if (priority(c) >priority(stack.peek())) {
stack.push(c);
}else{
while(!stack.isEmpty()&&priority(stack.peek())>=priority(c)){
sb.append(stack.pop());
}
stack.push(c);
}
}
}
case '('->{
stack.push(c);
}
case ')'->{
while(!stack.isEmpty()&&stack.peek()!='('){
sb.append(stack.pop());
}
stack.pop();
}
default ->{
sb.append(c);
}
}
}
while(!stack.isEmpty()){
sb.append(stack.pop());
}
return sb.toString();
}
}
E04Leetcode232
java
class MyQueue {
ArrayStack<Integer>s1 =new ArrayStack<>(100);
ArrayStack<Integer>s2 =new ArrayStack<>(100);
public void push(int x){//向队列尾添加
s2.push(x);
}
public int pop(){
if(s1.isEmpty()){
while(!s2.isEmpty()){
s1.push(s2.pop());
}
}
return s1.pop();
}
public int peek(){
if(s1.isEmpty()){
while(!s2.isEmpty()){
s1.push(s2.pop());
}
}
return s1.peek();
}
public boolean empty(){
return s1.isEmpty()&&s2.isEmpty();
}
static class ArrayStack<E>{
private E[] array;
private int top;//栈顶指针
public ArrayStack(int capacity) {
this.array = (E[])new Object[capacity];
}
public boolean push(E value) {
if(isFull()){
return false;
}
array[top++]=value;
return true;
}
public E pop() {
if(isEmpty()){
return null;
}
E value = array[top - 1];
top--;
return value;
}
public E peek() {
if(isEmpty()){
return null;
}
E value = array[top - 1];
return value;
}
public boolean isEmpty() {
return top==0;
}
public boolean isFull() {
return top==array.length;
}
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/