一、概述
说到栈,大家都不会陌生,相对于其他复杂的数据结构,栈相对来说概念上比较号理解,也比较简单,但是栈的具体应用却是非常灵活的,需要平时的积累和偶尔的灵感。栈(stack)只能对末尾的元素进行操作,该端称为栈顶,另一端称为栈尾。
大家想必都知道栈有后进先出(或先进后出)的特性,简称LIFO,不过很少记为FILO的,大致是约定俗成吧,就像队列是先进先出,记为FIFO,而大家很少会用LILO来表示,当然并不是完全没有啊,看到相应的表达还是要认识的。
二、实现
链表同顺序表一样,也有两种存储方法,分别是链栈和顺序栈。顺序栈的思路是通过数组等存储栈的元素,但是弊端在于数组的静态性,即如果数组初始长度过小,那么会出现存储不下的窘境;如果设置的过大则会造成内存的浪费。那么java集合Stack也是使用的数组,它是怎么做的呢?Stack继承于Vector,Vector使用的是扩容技术,即使用Arrays.copyOf()方法将数组赋给一个更大的数组,当然JDK是官方,对于我们实现如果能使用java.util.Arrays,那为什么不直接使用java.util.ArrayList替代数组呢?这样就没有动态扩容的问题了。当然都是可以实现的选项。
/*扩容的实现*/ stack = Arrays.copyOf(stack, newLength); //stack代表原数组,newLength代表新的长度,还有一个System.arrayCopy(); /*若新长度比原来大,剩下的值为默认值,否则截断旧数组*/*ArrayList实现栈*/package DS;
import java.util.ArrayList; import java.util.Iterator; import java.util.Stack; public class StackDemo<T> { /*Domain*/ private int size; private ArrayList <T> data; public StackDemo() { size = 0; data =new ArrayList<T>(); } public T push (T t) { data.add(t); size++; return t; } public T pop() {
if(size>0){ T end = data.remove(size-1); size--; return end;}
} public void tra() { Iterator it = data.iterator(); while(it.hasNext()) { System.out.print(it.next()); System.out.print("--"); } System.out.println(); } public static void main(String []args) { StackDemo <String> s = new StackDemo<>(); s.push("first"); s.push("second"); s.push("third"); s.push("forth"); s.tra(); System.out.println(s.pop()); s.tra(); s.pop(); s.tra(); // Stack } }
除了顺序栈,还有链栈的方法可选,即通过链表的方式把栈的各个元素串联起来,这样每次新数据入栈时只需申请新的节点即可,完全避免扩容的问题:
package DS; public class LinkedStack<T> { private int size; private Node end; private Node head; private class Node<T> { T data; Node next; Node prev; public Node (T t) { data =t; next =null; prev =null; } } public LinkedStack() { size =0; end = null; head = null; } public T push(T m) { Node n = new Node (m); if(size == 0) {head = end = n;size++;} else {end.next = n; n.prev = end; end = n; size++;} return m; } public T pop() { if(size>1) { T x = (T)end.data; end.prev.next=null; end = end.prev; size--; return x; } else if (size == 1) { T x = (T)end.data; head = end = null; size--; return x; } else { System.out.println("ss"); return null; } } public int getSize() { return size; } public void tr() { Node p = head; while (p!=null) { System.out.println(p.data); p = p.next; } } public static void main(String []args) { LinkedStack<Integer> ls = new LinkedStack<>(); ls.push(4); ls.push(5); ls.push(9); ls.tr(); System.out.println("The length is "+ls.getSize()); ls.pop(); ls.pop(); ls.tr(); System.out.println("The length is "+ls.getSize()); ls.pop(); ls.pop(); } }
三、实战
Valid Parentheses
Given a string containing just the characters '('
, ')'
, '{'
, '}'
, '['
and ']'
, determine if the input string is valid.
An input string is valid if:
- Open brackets must be closed by the same type of brackets.
- Open brackets must be closed in the correct order.
Note that an empty string is also considered valid.
Example 1:
Input: "()" Output: true
Example 2:
Input: "()[]{}" Output: true
Example 3:
Input: "(]" Output: false
Example 4:
Input: "([)]" Output: false
Example 5:
Input: "{[]}" Output: true
import java.util.Stack; class Solution { public boolean isValid(String s) { if (s.length()==0) return true; else if(s.length()%2==1) return false; else { Stack <Character>st = new Stack<Character>(); for (int i=0;i<s.length();i++) { if(st.isEmpty()) { st.push(s.charAt(i)); } else if ((char)st.peek() == '(') { if (s.charAt(i) == ')') st.pop(); else st.push(s.charAt(i)); } else if (st.peek() == '[') { if (s.charAt(i) == ']') st.pop(); else st.push(s.charAt(i)); } else if (st.peek() == '{') { if (s.charAt(i) == '}') st.pop(); else st.push(s.charAt(i)); } else { st.push(s.charAt(i)); } } return st.isEmpty(); } } }