1.概念
队列:只允许再一段进行插入数据的操作,再另一端进行删除数据的操作。队列具有先进先出FIFO(First In First Out)入队操作:进行插入操作的一段称为队尾,出队列:进行删除操作的一端称为队头
2.队列的使用
在Java中,Queue是一个接口,底层是通过链表来实现的,真正的实现这个接口的是LinkedList
【注意】Queue是一个接口,在实例化时必须实例化LinkedList的对象,因此LinkedList实现了Queue接口
接下来看一下Queue常用的方法:
//队列常用的方法
public class Test {
public static void main(String[] args) {
Queue<Integer> q = new LinkedList<>();
System.out.println("======================入队操作======================");
q.offer(1);
q.offer(2);
q.offer(3);
q.offer(4);
q.offer(5);
System.out.println(q);
System.out.println("=====================获取队头元素====================");
System.out.println(q.peek());
System.out.println("===================从队头出队========================");
System.out.println(q.poll());
System.out.println(q);
System.out.println("===================检测是否为空======================");
System.out.println(q.isEmpty());
System.out.println("===================输出元素个数======================");
System.out.println(q.size());
}
}
3.队列的模拟实现
队列中既可以储存元素,那底层肯定有储存元素的空间,通过前面线性表的学习,我们有两种数据结构可以选择:顺序结构 和 链式结构 。那么我们思考:队列的实现是用顺序结构好,还是线性结构好?
我们这里只用顺序结构来实现一下(顺序结构相对较难)
3.1循环队列
1.我们发现如果数组实现队列,如果进行出队操作,是从头出,这回导致前面的空间被释放后无法使用的问题。
2.我们设置一个循环队列,让其循环往复,这样就能使用被出队的空间了。
3.但是这样也会出现问题,如果元素被存满了,head=last,跟原先空的情况一样了。
4.我们可以采取两种方法:设置一个size或空出一个空间不存放,如果是下图这个样子,我们就认为它是满的。
3.2代码
package Demo;
/**
* Describe:简单模拟实现单端队列
* User:lenovo
* Date:2023-01-11
* Time:15:39
*/
public class MyQueue {
int[] array;
int length;
int head;
int last;
public MyQueue() {
array = new int[8];
head = 0;
last = 0;
length = 8;
}
//入队
public void offer(int val) {
if((last + 1) % length == head) {
throw new RuntimeException("空间已满");
}
array[last] = val;
last = (last + 1) % length;//防治下标超出范围;
}
//出队
public int poll() {
if(last == head) {
throw new NullPointerException("数组为空");
}
if(head < length - 1) {
head++;
return array[head - 1];
}else {
head = 0;
return array[length - 1];
}
}
//获取队首元素
public int peek() {
if(last == head) {
throw new NullPointerException("数组为空");
}
return array[head];
}
//获取有效元素个数
public int size() {
if(last > head || last == head) {
return last - head;
}else {
return last + length - head;
}
}
//判断是否为空
public boolean isEmpty() {
return head == last;
}
//测试
public static void main(String[] args) {
MyQueue q = new MyQueue();
q.offer(1);
q.offer(2);
q.offer(3);
q.offer(4);
q.offer(5);
q.offer(6);
q.offer(7);
//q.offer(8);
System.out.println("==================查看队首元素================");
System.out.println(q.peek());
System.out.println("==================出队=======================");
System.out.println(q.poll());
System.out.println(q.peek());
System.out.println();
System.out.println("==================检测能否入队========================");
q.offer(99);
System.out.println("==================全部出队===========================");
System.out.println(q.poll());
System.out.println(q.poll());
System.out.println(q.poll());
System.out.println(q.poll());
System.out.println(q.poll());
System.out.println(q.poll());
System.out.println(q.poll());
//System.out.println(q.poll());
System.out.println("==================元素个数==========================");
System.out.println(q.size());
System.out.println("==================检测是否为空=======================");
System.out.println(q.isEmpty());
}
}
4.双端队列(deque)(全称double ended queue)
双端队列是指两端都能够入队和出队操作的队列。Deque是一个接口,使用时必须要创建LinkedList的对象。
在实际的工程中,使用Deque接口比较多,栈和队列均可以使用该接口。
5.题目
使用两个队列实现一个后入先出的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
class MyStack {
private Queue<Integer> q1;
private Queue<Integer> q2;
public MyStack() {
q1 = new LinkedList<Integer>();
q2 = new LinkedList<Integer>();
}
public void push(int x) {
if(q2.isEmpty()) {
q1.offer(x);
}else {
q2.offer(x);
}
}
public int pop() {
if(!q1.isEmpty()) {
int size = q1.size();
while(size - 1 > 0) {
q2.offer(q1.poll());
size--;
}
return q1.poll();
}else if(!q2.isEmpty()) {
int size = q2.size();
while(size - 1 > 0) {
q1.offer(q2.poll());
size--;
}
return q2.poll();
}
return 0;
}
public int top() {
if(!q1.isEmpty()) {
int size = q1.size();
while(size - 1 > 0) {
q2.offer(q1.poll());
size--;
}
int ret = q1.poll();
q2.offer(ret);
return ret;
}else if(!q2.isEmpty()) {
int size = q2.size();
while(size - 1 > 0) {
q1.offer(q2.poll());
size--;
}
int ret = q2.poll();
q1.offer(ret);
return ret;
}
return 0;
}
public boolean empty() {
return q1.isEmpty() && q2.isEmpty();
}
}
首先创建两个队列;
入队时,那个队列有元素,入哪个队;如果都没元素,入q1;
出队的时候,我们要的是队尾的元素;我们让有元素的队列中的元素,出队,入队到另一个队列,直到原队列剩余一个元素。然后原队列出队操作,返回这个值;
两个队列都为空,返回空。
2.使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
//使用栈表示队列
class MyQueue {
Stack<Integer> s1;
Stack<Integer> s2;
public MyQueue() {
s1 = new Stack<>();
s2 = new Stack<>();
}
public void push(int x) {
while(!s1.empty()) {
s2.push(s1.pop());
}
s2.push(x);
while(!s2.empty()) {
s1.push(s2.pop());
}
}
public int pop() {
if(s1.isEmpty()) {
return 0;
}
return s1.pop();
}
public int peek() {
if(s1.isEmpty()) {
return 0;
}
int val = s1.peek();
return val;
}
public boolean empty() {
return s1.empty();
}
}
创建两个栈,s1表示出队的顺序,s2表示入队的顺序;
如果要入栈,把s1的元素转移到s2中,新的元素再放入s2,再将s2的全部元素转移到s1为出队的顺序;
入队和出队和查看队首就很好写了,同样判断是否为空也很好写了。