栈
概述
计算机科学中,stack 是一种线性的数据结构,只能在其一端添加数据和移除数据。习惯来说,这一端称之为栈顶 ,另一端不能操作数据的称之为栈底 ,就如同生活中的一摞书
先提供一个栈接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public interface Stack <E> { boolean push (E value) ; E pop () ; E peek () ; boolean isEmpty () ; boolean isFull () ; }
链表实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 public class LinkedListStack <E> implements Stack <E>, Iterable<E> { private final int capacity; private int size; private final Node<E> head = new Node <>(null , null ); public LinkedListStack (int capacity) { this .capacity = capacity; } @Override public boolean push (E value) { if (isFull()) { return false ; } head.next = new Node <>(value, head.next); size++; return true ; } @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 ; } @Override public boolean isFull () { return size == 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; } }; } static class Node <E> { E value; Node<E> next; public Node (E value, Node<E> next) { this .value = value; this .next = next; } } }
数组实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 public class ArrayStack <E> implements Stack <E>, Iterable<E>{ private final E[] array; private int top = 0 ; @SuppressWarnings("all") public ArrayStack (int capacity) { this .array = (E[]) new Object [capacity]; } @Override public boolean push (E value) { if (isFull()) { return false ; } array[top++] = value; return true ; } @Override public E pop () { if (isEmpty()) { return null ; } return array[--top]; } @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; } @Override public Iterator<E> iterator () { return new Iterator <E>() { int p = top; @Override public boolean hasNext () { return p > 0 ; } @Override public E next () { return array[--p]; } }; } }
应用
模拟如下方法调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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" ); }
模拟代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class CPU { static class Frame { int exit; public Frame (int exit) { this .exit = exit; } } static int pc = 1 ; 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; } } } } }
习题
E01. 有效的括号-Leetcode 20
一个字符串中可能出现 []
()
和 {}
三种括号,判断该括号是否有效
有效的例子
无效的例子
思路
遇到左括号, 把要配对的右括号放入栈顶
遇到右括号, 若此时栈为空, 返回 false,否则把它与栈顶元素对比
若相等, 栈顶元素弹出, 继续对比下一组
若不等, 无效括号直接返回 false
循环结束
若栈为空, 表示所有括号都配上对, 返回 true
若栈不为空, 表示右没配对的括号, 应返回 false
答案(用到了课堂案例中的 ArrayStack 类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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(); }
E02. 后缀表达式求值-Leetcode 120
后缀表达式也称为逆波兰表达式,即运算符写在后面
从左向右进行计算
不必考虑运算符优先级,即不用包含括号
示例
1 2 3 4 5 6 7 输入:tokens = ["2","1","+","3","*"] 输出:9 即:(2 + 1) * 3 输入:tokens = ["4","13","5","/","+"] 输出:6 即:4 + (13 / 5)
题目假设
数字都视为整数
数字和运算符个数给定正确,不会有除零发生
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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(); }
E03. 中缀表达式转后缀
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 public class E03InfixToSuffix { 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+(d*e+f)*g" )); } 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(); } static int priority (char c) { return switch (c) { case '(' -> 0 ; case '*' , '/' -> 2 ; case '+' , '-' -> 1 ; default -> throw new IllegalArgumentException ("不合法字符:" + c); }; } }
E04. 双栈模拟队列-Leetcode 232
给力扣题目用的自实现 栈,可以定义为静态内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class ArrayStack <E> { private E[] array; private int top; @SuppressWarnings("all") 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 ; } return array[--top]; } public E peek () { if (isEmpty()) { return null ; } return array[top - 1 ]; } public boolean isEmpty () { return top == 0 ; } public boolean isFull () { return top == array.length; } }
参考解答,注意:题目已说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class E04Leetcode232 { 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(); } }
E05. 单队列模拟栈-Leetcode 225
给力扣题目用的自实现 队列,可以定义为静态内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class ArrayQueue3 <E> { private final E[] array; int head = 0 ; int tail = 0 ; @SuppressWarnings("all") public ArrayQueue3 (int c) { c -= 1 ; c |= c >> 1 ; c |= c >> 2 ; c |= c >> 4 ; c |= c >> 8 ; c |= c >> 16 ; c += 1 ; array = (E[]) new Object [c]; } public boolean offer (E value) { if (isFull()) { return false ; } array[tail & (array.length - 1 )] = value; tail++; return true ; } public E poll () { if (isEmpty()) { return null ; } E value = array[head & (array.length - 1 )]; head++; return value; } public E peek () { if (isEmpty()) { return null ; } return array[head & (array.length - 1 )]; } public boolean isEmpty () { return head == tail; } public boolean isFull () { return tail - head == array.length; } }
参考解答,注意:题目已说明
调用 push、pop 等方法的次数最多 100
每次调用 pop 和 top 都能保证栈不为空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class E05Leetcode225 { ArrayQueue3<Integer> queue = new ArrayQueue3 <>(100 ); int size = 0 ; public void push (int x) { queue.offer(x); for (int i = 0 ; i < size; i++) { queue.offer(queue.poll()); } size++; } public int pop () { size--; return queue.poll(); } public int top () { return queue.peek(); } public boolean empty () { return queue.isEmpty(); } }
基础数据结构-队列
基础数据结构-双端队列