文章目录
java标准库中使用的栈
Stack的入栈和出栈的操作:
①把元素压栈:push(E);
②把栈顶的元素“弹出”:pop(E);
③取栈顶元素但不弹出:peek(E)
package java.util;
public class Stack<E> extends Vector<E> {
public Stack() {}
//入栈
public E push(E item) {
addElement(item);
return item;
}
//出栈
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
// 返回栈顶元素
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
}
java标准库中使用的队列
package java.util;
public interface Queue<E> extends Collection<E> {
// add()和offer()都是向队列中添加一个元素。一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,调用 add() 方法就会抛出一个 unchecked 异常,而调用 offer() 方法会返回 false。因此就可以在程序中进行有效的判断!
// 入队列
boolean add(E e);
// 入队列
boolean offer(E e);
// remove() 和 poll() 方法都是从队列中删除第一个元素。如果队列元素为空,调用remove() 的行为与 Collection 接口的版本相似会抛出异常,但是新的 poll() 方法在用空集合调用时只是返回 null。因此新的方法更适合容易出现异常条件的情况。
// 出队列
E remove();
// 出队列
E poll();
// element() 和 peek() 用于在队列的头部查询元素。与 remove() 方法类似,在队列为空时, element() 抛出一个异常,而 peek() 返回 null。
// 获取队头元素
E element();
// 获取队头元素
E peek();
// put 添加一个元素 如果队列满,则阻塞
// take 移除并返回队列头部的元素
}
package java.util;
public interface Deque<E> extends Queue<E> {}
// @since 1.6
public class ArrayDeque<E> extends AbstractCollection<E>
implements Deque<E>, Cloneable, Serializable{}
// @since 1.2
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}
JDK的官方文档不建议使用Stack实现栈的功能,转而使用Deque接口的ArrayDeque实现栈的功能。
当初 JDK1.0 在开发时,可能为了快速的推出一些基本的数据结构操作,所以推出了一些比较粗糙的类。比如,Vector、Stack、Hashtable等。这些类中的一些方法加上了 synchronized 关键字,容易给一些初级程序员在使用上造成一些误解!而且在之前的几个版本中,性能还不怎么好。
基于 Vector 实现的栈 Stack。底层实际上还是数组,所以还是存在需要扩容。Vector 是由数组实现的集合类,它包含了大量集合处理的方法。而 Stack 之所以继承 Vector,是为了复用 Vector 中的方法,来实现进栈(push)、出栈(pop)等操作。这里就是 Stack 设计不好的地方,既然只是为了实现栈,不用链表来单独实现,而是为了复用简单的方法而迫使它继承 Vector,Stack 和 Vector 本来是毫无关系的。这使得 Stack 在基于数组实现上效率受影响,另外因为继承 Vector 类,Stack 可以复用 Vector 大量方法,这使得 Stack 在设计上不严谨。
Java 提供了 Deuqe。Deque 是继承自 Queue,而 Stack 是继承自 Vector。Java 中的 Deuqe,即“double ended queue”的缩写,是 Java 中的双端队列集合类型。Deque 具备普通队列 FIFO 的功能,同时它也具备了 Stack 的 LIFO 功能,并且保留了 push 和 pop 函数,所以使用起来应该是一点障碍都没有。
ArrayDeque 是 Deque 接口的一种具体实现,是依赖于可变数组来实现的。ArrayDeque 没有容量限制,可根据需求自动进行扩容。ArrayDeque 可以作为栈来使用,效率要高于 Stack。ArrayDeque 也可以作为队列来使用,效率相较于基于双向链表的 LinkedList 也要更好一些。注意,ArrayDeque 不支持为 null 的元素。
接口方法对照:
Deque里也有offer(e)/poll()/peek()方法,底层还是调的offerLast(e)/pollFirst()/peekFirst()方法。
Deque里也有push()/pop()/peek()方法,底层还是调的addFirst()/removeFirst()/peekFirst()方法。
算法题中所用的单调栈、单调队列、优先级队列
单调栈:
Deque<Integer> stack = new ArrayDeque<Integer>();
// 栈顶加入
stack.push(i);
// 弹出栈顶
stack.pop();
// 返回栈顶
stack.peek()
单调队列
Deque<Integer> deque = new LinkedList<>();
// 返回队尾第一个元素
deque.peekLast();
// 删除队尾第一个元素
deque.pollLast();
// 从队尾加入一个元素
deque.offerLast(i);
// 返回队头第一个元素
deque.peekFirst();
// 删除队头第一个元素
deque.pollFirst();
优先级队列
// 小顶堆(默认)
PriorityQueue queue = new PriorityQueue<Integer>(
new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
}
);
// 大顶堆
PriorityQueue queue = new PriorityQueue<Integer>(
new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
);