栈(Stack)和队列(Queue)的相同点和不同点是什么?如何用两个栈实现队列(入队和出队)?
-
相同点:
- 栈和队列都属于线性表
- 栈和队列插入操作都是限定在线性表的头尾进行
- 栈和队列插入和删除的时间复杂度是O(1)
-
不同点:
- 特性不同,栈先进后出 (LIFO,Last In First Out) ;队列先进先出 (FIFO,First In First Out)
- 栈知识表的一端进行插入和删除操作,队列是在表的一端插入,另一端删除
- Java的栈(Stack)继承自 Vector,再往上的接口是 List/Collection;队列(Queue)直接继承自Collection接口
-
栈实现队列:
/** * 两个栈组成队列 */ public class StackQueue<E> { //入队操作的栈 private Stack<E> stack1 =new Stack<>(); //出队操作的栈 private Stack<E> stack2 =new Stack<>(); /** * 压入队列元素,只使用stack1 * @param element */ public void push(E element) { stack1.add(element); } /** * 取出队列顶部元素 * @return */ public E poll() { // stack2的数据为空时,才把stack1中的元素压入stack2(两种情况:1、初始化时两个栈的数据均为空;2、stack2数据出栈出完了 if (stack2.isEmpty()) { while (stack1.size() > 0) { stack2.add(stack1.pop()); } } //stack1的数据出栈完成后,stack2仍然为空,说明两个栈的数据都为空 if (stack2.isEmpty()) { throw new RuntimeException("queue is Empty!"); } E head = stack2.pop(); return head; } }
-
队列实现栈:
class MyStack { Queue<Integer> queue; /** Initialize your data structure here. */ public MyStack() { queue = new LinkedList<>(); } /** Push element x onto stack. */ public void push(int x) { queue.add(x); for (int i = 1; i < queue.size(); i++) { queue.add(queue.remove()); } } /** Removes the element on top of the stack and returns that element. */ public int pop() { return queue.poll(); } /** Get the top element. */ public int top() { return queue.peek(); } /** Returns whether the stack is empty. */ public boolean empty() { return queue.size() == 0; } }
-
栈和队列都属于线性表(linear list),而线性表是 n 个具有相同特性的数据元素的有限序列
Queue及常用子类可分三类:
- 双端队列(Deque):头部和尾部都支持插入和获取
- 阻塞队列(BlockingQueue):在元素的添加/删除操作时,如果没有成功,会阻塞等待执行。例如:当添加元素时,如果队列元素已满,队列会阻塞等待直到有空位时再插入
- 非阻塞队列(Non BlockingQueue):在元素的添加/删除操作时,如果没有成功,会直接返回操作的结果(通常双端队列也属于非阻塞队列)
- 常见的Queue的特性:
- PriorityBlockingQueue:带优先级的无界阻塞队列,元素按优先级顺序被移除,而不是先进先出队列(FIFO)。此外,队列中的元素要具有比较能力(用于判定优先级)。PriorityBlockingQueue 不允许 null 元素
- DelayQueue:存放 Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,DelayQueue 不允许使用 null 元素
- SynchronousQueue:同步阻塞队列,它的每个插入操作都要等待其他线程相应的移除操作,反之亦然。它的一个典型应用场景是 Executors.newCachedThreadPool () ,在新任务到来时创建新的线程,如果有空闲线程则会重复使用,SynchronousQueue 不允许 null 元素
LinkedHashMap和PriorityQueue的区别?
- PriorityQueue 是一个优先级队列,保证最高或者最低优先级的的元素总是在队列头部,但是 LinkedHashMap 维持的顺序是元素插入的顺序。当遍历一个 PriorityQueue 时,没有任何顺序保证,但是 LinkedHashMap 课保证遍历顺序是元素插入的顺序
Queue中add/offer方法的区别?
- add()和offer()都是向队列中添加一个元素。一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,调用 add() 方法就会抛出IllegalStateException异常,而调用 offer() 方法会返回 false
Queue中remove()/poll()方法的区别?
- poll()/remove()方法都是从队列中删除头部元素。如果队列元素为空,调用remove() 则会抛出NoSuchElementException,而poll() 方法在用空集合调用时只是返回 null。
Queue中element()/peek()方法的区别?
- element() 和 peek() 用于在队列的头部查询元素。与 remove() 方法类似,在队列为空时, element() 抛出NoSuchElementException异常,而 peek() 返回 null
BlockingQueue有哪些实现类?ArrayBlockingQueue: 由数组组成的有界阻塞队列?
- LinkedBlockingQueue: 由链表组成的有界阻塞队列(默认大小为 Integer.MAX_VALUE)
- PriorityBlockingQueue:支持优先级排序的无界阻塞队列
- DelayQueue:使用优先级队列实现的延迟无界阻塞队列
- SynchronousQueue: 不存储元素的阻塞队列,单个元素的队列,同步提交队列
- LinkedTransferQueue:链表组成的无界阻塞队列
- LinkedBlockingDeque:链表组成的双向阻塞队列
文章推荐:
- 无锁队列:无锁队列总结
- 线性结构系列: 线性表 及Java实现 顺序表、链表、栈、队列