文章目录
- 前言
- 1.队列(Queue)的概念
- 2.Queue的使用
- 3.队列的模拟实现
- 4.循环队列
- 5.双端队列
- 6.面试题
- [1. 用队列实现栈](https://leetcode.cn/problems/implement-stack-using-queues/description/)
- [2. 用栈实现队列](https://leetcode.cn/problems/implement-queue-using-stacks/description/)
前言
上一篇我们学习了栈(Stack) ,现在讲队列(Queue)。
1.队列(Queue)的概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front)
2.Queue的使用
在Java中,Queue是个接口,底层是通过链表实现的。
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<>();
queue.offer(1);
queue.offer(2);
queue.offer(3);
queue.offer(4);
queue.offer(5); // 从队尾入队列
System.out.println(queue.size());
System.out.println(queue.peek()); // 获取队头元素
queue.poll();
System.out.println(queue.poll()); // 从队头出队列,并将删除的元素返回
if(queue.isEmpty()){
System.out.println("队列空");
}else{
System.out.println(queue.size());
}
}
3.队列的模拟实现
队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有两种:顺序结构 和 链式结构。思考下:队列的实现使用顺序结构还是链式结构好?
public class MyQueue {
static class ListNode{
public int val;
public ListNode prev;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode first;
public ListNode last;
public int usedSize = 0;
public void offer(int data){
ListNode node = new ListNode(data);
if (isEmpty()){
first = last = node;
}else {
last.next = node;
node.prev = last;
last = node;
}
this.usedSize++;
}
public int poll(){
if (isEmpty()){
return -1;
}
int val = first.val;
first = first.next;
if (first!=null){
first.prev = null;
}
this.usedSize--;
return val;
}
public int peek(){
if (isEmpty()){
return -1;
}
return first.val;
}
public boolean isEmpty(){
return usedSize == 0;
}
}
4.循环队列
如何使用顺序表来模拟队列?
使用循环队列。
实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。环形队列通常使用数组实现。
有下面三个问题需要先解决:
- 怎么判断数组已经满了?
- 怎么判断数组是空的?
- 如何使数组下标进行循环?
在这我们使用方案二进行操作。
设计循环队列
class MyCircularQueue {
public int rear;
public int front;
public int[]elem;
public MyCircularQueue(int k) {
elem = new int[k+1];
}
public boolean enQueue(int value) {
if (isFull()){
return false;
}
elem[rear] = value;
rear = (rear + 1) % elem.length;
return true;
}
public boolean deQueue() {
if (isEmpty()){
return false;
}
front = (front + 1) % elem.length;
return true;
}
public int Front() {
if (isEmpty()){
return -1;
}
return elem[front];
}
public int Rear() {
if (isEmpty()){
return -1;
}
int index = (rear == 0)? elem.length-1 : rear - 1;
return elem[index];
}
public boolean isEmpty() {
return rear == front;
}
public boolean isFull() {
return (rear+1)%elem.length == front;
}
}
5.双端队列
双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。
那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
Deque是一个接口,使用时必须创建LinkedList的对象。
在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口
Deque< Integer > stack = new ArrayDeque<>();//双端队列的线性实现
Deque< Integer > queue = new LinkedList<>();//双端队列的链式实现
6.面试题
1. 用队列实现栈
class MyStack {
public Queue<Integer> queue1;
public Queue<Integer> queue2;
public MyStack() {
queue1 = new LinkedList();
queue2 = new LinkedList();
}
public void push(int x) {
if (!queue1.isEmpty()){
queue1.offer(x);
}else if (!queue2.isEmpty()){
queue2.offer(x);
}else{
queue1.offer(x);
}
}
public int pop() {
if (empty()){
return -1;
}
if (!queue1.isEmpty()){
int size = queue1.size();
for (int i=0;i<size-1;i++){
queue2.offer(queue1.poll());
}
return queue1.poll();
}else{
int size = queue2.size();
for(int i = 0;i<size-1;i++){
queue1.offer(queue2.poll());
}
return queue2.poll();
}
}
public int top() {
if (empty()){
return -1;
}
if (!queue1.isEmpty()){
int size = queue1.size();
int val = 0;
for (int i = 0; i< size;i++){
val = queue1.poll();
queue2.offer(val);
}
return val;
}else{
int size = queue2.size();
int val = 0;
for(int i = 0;i<size;i++){
val = queue2.poll();
queue1.offer(val);
}
return val;
}
}
public boolean empty() {
return queue1.isEmpty()&&queue2.isEmpty();
}
}
2. 用栈实现队列
class MyQueue {
public ArrayDeque<Integer> stack1;
public ArrayDeque<Integer> stack2;
public MyQueue() {
stack1 = new ArrayDeque();
stack2 = new ArrayDeque();
}
public void push(int x) {
stack1.push(x);
}
public int pop() {
if (empty()){
return -1;
}
if (stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
public int peek() {
if (empty()){
return -1;
}
if (stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.peek();
}
public boolean empty() {
return stack1.isEmpty()&&stack2.isEmpty();
}
}
下一篇便要进入现阶段比较难的二叉树的部分,大家做好准备,开干!