【本节目标】
1.
队列的概念及使用
2.
相关
OJ
题
一、 概念
队列
:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
FIFO(FirstIn First Out)
入队列:进行插入操作的一端称为
队尾(
Tail/Rear
)
出队列:进行删除操作的一端称为
队头
(
Head/Front
)
二、 队列的使用
在
Java
中,
Queue
是个接口,底层是通过链表实现
的。
注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。
Queue<Integer> queue=new LinkedList<>();
三、队列模拟实现
队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有两种:顺序结构 和 链式结构
。同学们思考下:
队列的实现使用顺序结构还是链式结构好?
- 单链表加上一个尾节点的引用
- 入队采用尾插法,出队删除头结点
- 注意:就算有队尾标记,也不能从头结点入队,尾结点出队
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=null;
public ListNode last=null;
public int usedSize=0;
public void offer(int val){
ListNode cur=new ListNode(val);
if(isEmpty()){
first= last=cur;
usedSize++;
}else {
last.next=cur;
cur.prev=last;
last=cur;
}
usedSize++;
}
public int poll(){
int popElement=first.val;
if(isEmpty()){
return -1;
}
first=first.next;
if(first!=null){
first.prev=null;
}
usedSize--;
return popElement;
}
public int peek(){
if(isEmpty()){
return -1;
}else {
return first.val;
}
}
public boolean isEmpty(){
return usedSize==0;
}
}
四、循环队列
实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。环形队列通常使用数组实现。
数组下标循环的小技巧
1.
下标最后再往后
(offset
小于
array.length): index = (index + offset) % array.length
2. 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset) % array.length
如何区分空与满
1.
通过添加
size
属性记录
2.
保留一个位置
3.
使用标记
class MyCircularQueue {
public int front;
public int rear;
public int[] elem;
public MyCircularQueue(int k) {
elem=new int[k+1];
}
public boolean enQueue(int value) {
if(isFull()){
return false;
}else{
elem[rear]=value;
rear=(rear+1)%elem.length ;
return true;
}
}
public boolean deQueue() {
if(isEmpty()){
return false;
}else{
front=(front+1)%elem.length;
return true;
}
}
public int Front() {
if(isEmpty()){
return -1;
}else{
return elem[front];
}
}
public int Rear() {
if(isEmpty()){
return -1;
}else{
int k=(rear+elem.length-1)%elem.length;
return elem[k];
}
}
public boolean isEmpty() {
return front==rear;
}
public boolean isFull() {
return (rear+1)%elem.length==front;
}
}
/**
* Your MyCircularQueue object will be instantiated and called as such:
* MyCircularQueue obj = new MyCircularQueue(k);
* boolean param_1 = obj.enQueue(value);
* boolean param_2 = obj.deQueue();
* int param_3 = obj.Front();
* int param_4 = obj.Rear();
* boolean param_5 = obj.isEmpty();
* boolean param_6 = obj.isFull();
*/
五. 双端队列 (Deque)
双端队列(deque
)是指允许两端都可以进行入队和出队操作的队列,
deque
是
“double ended queue”
的简称。那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
Deque
是一个接口,使用时必须创建
LinkedList
的对象。
在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口
Deque<Integer> stack = new ArrayDeque<>();// 双端队列的线性实现Deque<Integer> queue = new LinkedList<>();// 双端队列的链式实现
六. OJ题
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(!queue1.isEmpty()){
queue2.offer(x);
}else{
queue1.offer(x);
}
}
public int pop() {
if(empty()){
return -1;
}else 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;
}else if(!queue1.isEmpty()){
int size=queue1.size();
for(int i=0;i<size-1;i++){
queue2.offer(queue1.poll());
}
int x=queue1.poll();
queue2.offer(x);
return x;
}else{
int size=queue2.size();
for(int i=0;i<size-1;i++){
queue1.offer(queue2.poll());
}
int x=queue2.poll();
queue1.offer(x);
return x;
}
}
public boolean empty() {
return queue1.isEmpty()&&queue2.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
2. 用栈实现队列。
import java.util.ArrayDeque;
class MyQueueUseStack {
public ArrayDeque<Integer> stack1;
public ArrayDeque<Integer> stack2;
public MyQueueUseStack() {
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();
}
}