只允许在一端进行插入数据操作,在另一端进行删除数据操作得特殊线性表,队列是先进先出,入队:进行插入操作得一端称为队尾(rear),出队:进行删除操作的一端称为队头(front)。队列Queue是个接口,底层通过链表实现的。
注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。
public class MyQueue {
static class ListNode {
public int val;
public ListNode prev;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head; // 队头
public ListNode last; // 队尾
}
public boolean empty() {
return head == null;
}
public int peek() {
if (head == null) {
return -1;
}
return head.val;
}
入队相当于链表的尾插法,入队元素node,然后队列为空,直接把头尾节点指向node,不为空:从队尾入队操作,last的后节点指向node,node的前节点指向last,last再指向node。
public void offer(int val) {
ListNode node = new ListNode(val);
if (head == null) {
head = last = node;
}else {
last.next = node;
node.prev = last;
last = node;
}
}
给个临时变量val,当只有一个节点时:val指向head.val,出队完head指向null,返回val。不止一个节点:val指向head.val,head指向head的后节点,head的前驱节点指向null,返回val。
public int poll() {
if(head == null) {
return -1;
}
//只有一个节点
int val = -1;
if (head.next == null) {
val = head.val;
head = null;
return val;
}
val = head.val;
head = head.next;
head.prev = null;
return val;
}
假如队列的大小为8,当7走到下一个下标0的时候,下标+1实现不了,所有在循环队列当中,实现数组下标循环:队头:front = (front + 1) % elem.length,队尾:rear = (rear + 1) % elem.length,区分队列空和满:当队头front和队尾rear相遇时为空。当队尾rear的下个为队头front为满。
class MyCircularQueue {
// 循环队列
public int[] elem;
public int front;
public int rear;
public MyCircularQueue(int k) {
elem = new int[k+1];
}
}
public boolean isEmpty() {
return front == rear;
}
public boolean isFull() {
return (rear+1) % elem.length == front;
}
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 {
// 得到数组下标最后再完后的下标 如果最大是8,后面跳转到0
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];
}
在这里用到两个先进先出的队列来实现栈。
思路:
class MyStack{
private Queue<Integer> qu1;
private Queue<Integer> qu2;
public MyStack() {
qu1 = new LinkedList<>();
qu2 = new LinkedList<>();
}
}
判断栈是否为空我们直接返回两个队列是否为空。
public boolean empty() {
return qu1.isEmpty() && qu2.isEmpty();
}
当两个队列都是空的时候,放到第一个队列,再次入栈的时候,放到不为空的队列
public void push(int x) {
if (empty()) {
qu1.offer(x);
return;
}
if (!qu1.isEmpty()) {
qu1.offer(x);
}else {
qu2.offer(x);
}
}
找到不为空的队列 出size-1个元素 剩下的元素就是出栈的元素,每弹出一个size都在变小一次,
public int pop() {
if (empty()) {
return -1;
}
// 找到不为空的队列 出size-1个元素 剩下的元素就是出栈的元素
if (!qu1.isEmpty()) {
int size = qu1.size();
for (int i = 0; i < size-1; i++) {
qu2.offer(qu1.poll());
}
return qu1.poll();
}else {
// 没弹出一个size都在变小一次
int size = qu2.size();
for (int i = 0; i < size-1; i++) {
qu1.offer(qu2.poll());
}
return qu2.poll();
}
}
和出栈一样,给一个临时变量tmp,用tmp接收队列的最后一个元素。
public int top() {
if (empty()) {
return -1;
}
if (!qu1.isEmpty()) {
int size = qu1.size();
int tmp = -1;
for (int i = 0; i < size; i++) {
tmp = qu1.poll();
qu2.offer(tmp);
}
return tmp;
}else {
// 没弹出一个size都在变小一次
int size = qu2.size();
int tmp = -1;
for (int i = 0; i < size; i++) {
tmp = qu2.poll();
qu1.offer(tmp);
}
return tmp;
}
}
用栈模拟实现队列也需要用到两个栈,思路:
class MyQueue {
private Stack<Integer> st1;
private Stack<Integer> st2;
public MyQueue() {
st1 = new Stack<>();
st2 = new Stack<>();
}
}
入栈直接全部放进第一个栈中
public void push(int x) {
st1.push(x);
}
把第一个栈的全部元素放进第二栈当中,出st2这个栈当中的栈顶元素即可,如果st2为空,把st1里面所有的元素全部放到st2
public int pop() {
if(empty()) {
return -1;
}
if(st2.empty()) {
while(!st1.empty()) {
st2.push(st1.pop());
}
}
return st2.pop();
}
和出队一样,peek第二个栈的栈顶元素即可
public int peek() {
if(empty()) {
return -1;
}
if(st2.empty()) {
while(!st1.empty()) {
st2.push(st1.pop());
}
}
return st2.peek();
}
public boolean empty() {
return st1.empty() && st2.empty();
}