栈介绍
- 栈 (stack)只允许在有序的线性数据集合的一端(称为栈顶 top)进行加入数据(push)和移除数据(pop)。因而按照 后进先出(LIFO, Last In First Out) 的原理运作。在栈中,push 和 pop 的操作都发生在栈顶。
- 栈常用一维数组或链表来实现,用数组实现的栈叫作 顺序栈 ,用链表实现的栈叫作 链式栈 。
假设堆栈中有n个元素。
访问:O(n)//最坏情况
插入删除:O(1)//顶端插入和删除元素
栈的实现
- 栈既可以通过数组实现,也可以通过链表来实现。不管基于数组还是链表,入栈、出栈的时间复杂度都为 O(1)。
- 栈具有
push()
、pop()
(返回栈顶元素并出栈)、peek()
(返回栈顶元素不出栈)、isEmpty()
、size()
这些基本的方法。 - 用数组实现要注意扩容
public class MyStack {
private int[] storage;//存放栈中元素的数组
private int capacity;//栈的容量
private int count;//栈中元素数量
//扩容时的倍数
private static final int GROW_FACTOR = 2;
//不带初始容量的构造方法。默认容量为8
public MyStack() {
this.capacity = 8;
this.storage=new int[8];
this.count = 0;
}
//带初始容量的构造方法
public MyStack(int initialCapacity) {
if (initialCapacity < 1)
throw new IllegalArgumentException("Capacity too small.");
this.capacity = initialCapacity;
this.storage = new int[initialCapacity];
this.count = 0;
}
//入栈
public void push(int value) {
if (count == capacity) {
ensureCapacity();
}
storage[count++] = value;
}
//确保容量大小
private void ensureCapacity() {
int newCapacity = capacity * GROW_FACTOR;
//扩容
storage = Arrays.copyOf(storage, newCapacity);
capacity = newCapacity;
}
//返回栈顶元素并出栈
private int pop() {
if (count == 0)
throw new IllegalArgumentException("Stack is empty.");
count--;
return storage[count];
}
//返回栈顶元素不出栈
private int peek() {
if (count == 0){
throw new IllegalArgumentException("Stack is empty.");
}else {
return storage[count-1];
}
}
//判断栈是否为空
private boolean isEmpty() {
return count == 0;
}
//返回栈中元素的个数
private int size() {
return count;
}
}
常见应用
反转字符串
让字符串入栈再出栈即可
浏览器的回退和前进
可以用两个栈来实现。
假设我们之前浏览了1、2、3、4四个界面,后退两个可以出栈4、3,再入栈到stack2中,前进就可以从stack出栈,入栈stack1中。
检查符号是否成对出现
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断该字符串是否有效。
有效字符串需满足:
1.左括号必须用相同类型的右括号闭合。
2.左括号必须以正确的顺序闭合。
比如 “()”、"()[]{}"、"{[]}" 都是有效字符串,而 “(]” 、"([)]" 则不是。
思路:
- 首先我们将括号间的对应规则存放在 Map 中
- 创建一个栈。遍历字符串,如果字符是左括号就直接加入stack中,否则将stack 的栈顶元素与这种括号的右括号做比较,如果不相等就直接返回 false。遍历结束,如果stack为空,返回 true。
public boolean isValid(String s){
// 括号之间的对应规则
HashMap<Character, Character> mappings = new HashMap<Character, Character>();
//索引为右括号,值为左括号
mappings.put(')', '(');
mappings.put('}', '{');
mappings.put(']', '[');
Stack<Character> stack = new Stack<Character>();
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
//key中包含就是右括号
if (mappings.containsKey(chars[i])) {
//取出栈顶元素
char topElement = stack.empty() ? '#' : stack.pop();
//利用右括号取出左括号与栈顶元素对比
if (topElement != mappings.get(chars[i])) {
return false;
}
} else {
stack.push(chars[i]);
}
}
return stack.isEmpty();
}