目录
队列
特点: 先进先出的结构
和现实生活中 排队 的情况类似
队列实现
队列中的方法
双向链表实现
初始化
static class ListNode {
public int val;
public ListNode next;
public ListNode prev;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
public ListNode last;
public int usedSize = 0;
入队列
@Override
public boolean offer(int val) {
ListNode node = new ListNode(val);
if(head == null) {
head = node;
last = node;
}else {
last.next = node;
node.prev = last;
last = last.next;
}
usedSize++;
return true;
}
出队列
@Override
public int poll() {
if(head == null) {
return -1;
}
int retVal = head.val;
if (head.next == null) {
head = null;
last = null;
return retVal;
}
head = head.next;
head.prev = null;
return retVal;
}
获取队头元素但不删除
@Override
public int peek() {
if(head == null) {
return -1;
}
if (head.next == null) {
int retVal = head.val;
head = null;
last = null;
return retVal;
}
return head.val;
}
获取有效元素个数
@Override
public int size() {
return usedSize;
}
检测队列是否为空
@Override
public boolean isEmpty() {
return head == null;
}
注意
1> 使用双向链表实现队列.
2> 在入队时判断是否队列为空.
3> 出队时先判断是否队列为空, 然后判断是否只有头结点.
数组实现循环队列
什么是循环队列?
我们使用数组来实现队列
现在队列满了之后, 我们可以出队列, 出去一个就让front++.
这种情况下入队列时rear++, 会数组越界, 我们选择让rear重新回到起点
这样就构成了一个循环, 不但rear这样, 让front也这样.这样可以用数组实现一个队列
如下图:
接下来解决其他问题:
1> 当rear 和 front 相遇时是什么情况? 队列是满的还是空的?
有多种方案:
1. 使用 usedSize 记录
2. 浪费一个空间表示满 (rear里面放数据时判断下一个值是不是front)
3. 使用标记 (第一次相遇标记一下,第二次标记一下)
2> 怎么让rear 和 front 从 7 下标来到 0 下标?
使用 % 的方法
package queuedemo;
public class MyCircularQueue implements IQueue {
private int[] elem;
private int front = 0; // 对头
private int rear = 0; // 队尾
public MyCircularQueue(int k) {
this.elem = new int[k+1];
}
@Override
public boolean offer(int x) {
if(isFull()) {
return false;
}else {
elem[rear] = x;
rear = (rear+1)%elem.length;
return true;
}
}
@Override
public int poll() {
if(isEmpty()) {
return -1;
}else {
int old = elem[front];
front = (front+1)%elem.length;
return old;
}
}
@Override
public int peek() {
if(isEmpty()) {
return -1;
}
return elem[front];
}
@Override
public int size() {
return (rear-front);
}
@Override
public boolean isEmpty() {
return front == rear;
}
public boolean isFull() {
return (rear+1)%elem.length == front;
}
}
双端队列(Deque)
在队列两边都可以进行入队和出队的操作
Deque 是一个接口.
Deque<Integer> deque = new ArrayDeque<>();
Deque<Integer> deque1 = new LinkedList<>();
其中有很多方法: 从头删除元素, 从队尾删除元素...
这俩结构不仅可以当作队列, 还可以当作栈.
两个例题
用队列实现栈
需要用两个队列
1> 哪个队列不为空放到哪个队列中
2> 出栈的时候那个不为空出哪个 size-1
3> 当两个队列都是空的时候, 说明模拟栈是空的
代码实现:
import java.util.LinkedList;
import java.util.Queue;
public class MyStack {
private Queue<Integer> qu1;
private Queue<Integer> qu2;
public MyStack() {
qu1 = new LinkedList<>();
qu2 = new LinkedList<>();
}
public void push(int x) {
if(!qu1.isEmpty()) {
qu1.offer(x);
}else if (!qu2.isEmpty()) {
qu2.offer(x);
}else {
qu1.offer(x);
}
}
public int pop() {
if(empty()) {
return -1;
}
// 判断是否为空
if(!qu1.isEmpty()) {
// 注意 size 是会变的, 所以创建一个变量放在外面
int size = qu1.size()-1;
for(int i = 0; i < size; i++) {
int x = qu1.poll();
qu2.offer(x);
}
return qu1.poll();
} else {
int size = qu2.size()-1;
for(int i = 0; i < size; i++) {
int x = qu2.poll();
qu1.offer(x);
}
return qu2.poll();
}
}
public int top() {
if(empty()) {
return -1;
}
if(!qu1.isEmpty()) {
int size = qu1.size()-1;
for(int i = 0; i < size; i++) {
int x = qu1.poll();
qu2.offer(x);
}
int tmp = qu1.poll();
qu2.offer(tmp);
return tmp;
} else {
int size = qu2.size()-1;
for(int i = 0; i < size; i++) {
int x = qu2.poll();
qu1.offer(x);
}
int tmp = qu2.poll();
qu1.offer(tmp);
return tmp;
}
}
// 两个队列都为空时, 模拟实现的栈是空的
public boolean empty() {
return qu1.isEmpty() && qu2.isEmpty();
}
}
用栈实现队列
1> 入队的时候放到第一个栈里面
2> 出队的时候出第 2 个栈中的元素, 当第 2 个栈中没有元素时把第一个栈中的元素倒过来
import java.util.Stack;
public class MyQueue {
private Stack<Integer> s1;
private Stack<Integer> s2;
public MyQueue() {
s1 = new Stack<>();
s2 = new Stack<>();
}
public void push(int x) {
s1.push(x);
}
public int pop() {
if(empty()) {
return -1;
}
if(!s2.isEmpty()) {
return s2.pop();
}
while(!s1.isEmpty()) {
int x = s1.pop();
s2.push(x);
}
return s2.pop();
}
public int peek() {
if(empty()) {
return -1;
}
if(!s2.isEmpty()) {
return s2.peek();
}
while(!s1.isEmpty()) {
int x = s1.pop();
s2.push(x);
}
return s2.peek();
}
public boolean empty() {
return s1.isEmpty() && s2.isEmpty();
}
}