概述
计算机科学中,stack是一种线性的数据结构,只能在其一段添加和移除数据.
习惯来说,这一端称之为栈顶,另一端不能操作数据的称之为栈底,就如同生活中的一摞书
先提供一个接口:
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();
}
链表实现:
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;}@Overridepublic Iterator<E> iterator() {return new Iterator<E>() {Node<E> p = head.next;@Overridepublic boolean hasNext() {return p!=null;}@Overridepublic E next() {E value = p.value;p=p.next;return value;}};}
/*2->head -> 2 -> 1 ->null*/@Overridepublic boolean push(E value) {if(isFull()){return false;}head.next = new Node<>(value,head.next);size++;return true;}/*head -> 2 ->1 ->null*/@Overridepublic E pop() {if(isEmpty()){return null;}Node<E> first = head.next;head.next = first.next;size--;return first.value;}@Overridepublic E peek() {if(isEmpty()){return null;}return head.next.value;}@Overridepublic boolean isEmpty() {return head.next ==null;//也可以size==0}@Overridepublic 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;}}}
数组实现:
import java.util.Iterator;public class ArrayStack<E> implements Stack<E>,Iterable<E> {private E[] array;private int top;//栈顶指针/*底 顶0 1 2 3a b c dt数组实现跟链表实现的顶部是相反的为什么链表的栈顶在首节点?因为增删查改方便,如果放在链表尾部那每次增删查改都需要遍历为什么数组实现的栈顶在尾部,因为数组是顺序结构*/@SuppressWarnings("all")public ArrayStack(int capacity) {this.array = (E[])new Object[capacity];}@Overridepublic Iterator<E> iterator() {return new Iterator<E>() {int p = top;@Overridepublic boolean hasNext() {return p>0;}@Overridepublic E next() {E value = array[p - 1];p--;//array[--p];return value;}};}@Overridepublic boolean push(E value) {if(isFull()) {return false;}array[top] = value;top++;//array[top++] = value;return true;}@Overridepublic E pop() {if(isEmpty()){return null;}E value = array[top-1];top --;//array[--top];return value;}@Overridepublic E peek() {if(isEmpty()){return null;}return array[top-1];}@Overridepublic boolean isEmpty() {return top == 0;}@Overridepublic boolean isFull() {return top == array.length;}
}
应用
模拟如下方法调用:
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*/
模拟实现:
public class CPU {static class Frame {int exit;public Frame(int exit) {this.exit = exit;}}static int pc = 1; // 模拟程序计数器 Program counterstatic 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*/
练习一下:
20. 有效的括号 - 力扣(LeetCode)
E01. 有效的括号-Leetcode 20
一个字符串中可能出现 []
()
和 {}
三种括号,判断该括号是否有效
有效的例子
()[]{} ([{}]) ()
无效的例子
[) ([)] ([]
思路
-
遇到左括号, 把要配对的右括号放入栈顶
-
遇到右括号, 若此时栈为空, 返回 false,否则把它与栈顶元素对比
-
若相等, 栈顶元素弹出, 继续对比下一组
-
若不等, 无效括号直接返回 false
-
-
循环结束
-
若栈为空, 表示所有括号都配上对, 返回 true
-
若栈不为空, 表示右没配对的括号, 应返回 false
-
答案(用到了上面案例中的 ArrayStack 类)
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();}
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;}
}
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)
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
看不懂也没有关系,反正编译器会讲中缀表达式转换为后缀表达式
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
232. 用栈实现队列 - 力扣(LeetCode)
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();*/