1.队列
1.1 概念
- 是只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表
- 遵循先进先出的原则(举例:在食堂排队打饭)
- 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
1.2 队列的使用
在Java中,Queue是个接口,底层是通过链表实现的(数组也可以实现队列)
用代码实现:
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<>();
queue.offer(1);
queue.offer(2);
int val = queue.poll();
System.out.println(val);
int val2 = queue.peek();
System.out.println(val2);
}
注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口
- 下面看单链表和双向链表实现队列的时间复杂度:
1.3 队列的模拟实现
public class MyQueue {
static class ListNode {
public int val;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
public ListNode last;
private int usedSize;
public void offer(int val) {
ListNode node = new ListNode(val);
if(head == null) {
head = node;
last = node;
}else {
last.next = node;
last = last.next;
}
usedSize++;
}
public int getUsedSize() {
return usedSize;
}
public int poll() {
if(head == null) {
return -1;
}
int val = -1;
if(head.next == null) {
val = head.val;
head = null;
last = null;
return val;
}
val = head.val;
head = head.next;
usedSize--;
return val;
}
public int peek() {
if(head == null) {
return -1;
}
return head.val;
}
}
1.4 循环队列
class MyCircularQueue {
private int[] elem;
private int front;//队头下标
private int rear;//队尾下标
public MyCircularQueue(int k) {
this.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;
}
}
注意:因为此方法浪费了一个空间,所以数组的大小是k+1
1.5 双端队列(Deque)
- 允许两端都可以进行入队和出队操作的队列
- Deque是一个接口,使用时必须创建LinkedList的对象。
栈和队列均可以使用Deque接口:
Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现
1,6 栈和队列(面试题)
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()) {
int currentSize = qu1.size();
for (int i = 0; i < currentSize-1; i++) {
int x = qu1.poll();
qu2.offer(x);
}
return qu1.poll();//最后一个数据返回
}
if(!qu2.isEmpty()) {
int currentSize = qu2.size();
for (int i = 0; i < currentSize-1; i++) {
int x = qu2.poll();
qu1.offer(x);
}
return qu2.poll();//最后一个数据返回
}
return -1;
}
//peek方法
public int top() {
if(empty()) {
return -1;
}
if(!qu1.isEmpty()) {
int currentSize = qu1.size();
int x = -1;
for (int i = 0; i < currentSize; i++) {
x = qu1.poll();
qu2.offer(x);
}
return x;//最后一个数据返回
}
if(!qu2.isEmpty()) {
int currentSize = qu2.size();
int x = -1;
for (int i = 0; i < currentSize; i++) {
x = qu2.poll();
qu1.offer(x);
}
return x;//最后一个数据返回
}
return -1;
}
public boolean empty() {
return qu1.isEmpty() && qu2.isEmpty();
}
}
注意:
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(!s2.empty()) {
return s2.pop();
}else {
while(!s1.empty()) {
int val = s1.pop();
s2.push(val);
}
return s2.pop();
}
}
public int peek() {
if(!s2.empty()) {
return s2.peek();
}else {
while(!s1.empty()) {
int val = s1.pop();
s2.push(val);
}
return s2.peek();
}
}
public boolean empty() {
return s1.empty() && s2.empty();
}
}