目录
1.队列的基本概念
1.只允许在一端(队尾)进行数据插入,在另一端(对头)进行数据删除,遵循先进先出(First In First Out,FIFO)原则;
1.1.基本方法:栈的几种主要基本操作:
入队列:add()、offer()
出队列:remove()、poll()
获取队首元素:element()、peek()
注意:虽然每种操作对应两种方式,但一般常用后者方法进行操作,因为在有容量限制时,前者会报错,而后者会返回一个null值。
2.队列的实现
队列可用数组和链表的结构实现,使用链表的结构实现更优一些,若使用数组的结构,由于出队列在数组头上出数据,但效率较低。
如果是单链表作为一个队列------->要考虑对头在那边,队尾在那边
1.队列方法的实现
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<>(); //队列,尾进头出
queue.add(1); //有容量限制时,add会抛异常,而offer不抛异常
queue.offer(2); //添加元素 --》offer常用
System.out.println(queue); //[1, 2]
System.out.println(queue.peek()); //1---》获取队头元素
System.out.println(queue.element()); //1
System.out.println("======================");
System.out.println(queue.remove()); //出队列 ,从头开始出
System.out.println(queue.poll()); //
System.out.println("======================");
Deque<Integer> deque1 = new LinkedList<>(); //双端队列 :两边都可进出
deque1.offer(2); //默认对尾插
deque1.offerFirst(1); //头插
deque1.offerLast(3); //尾插
System.out.println(deque1); //[1, 2, 3]
LinkedList<Integer> deque2 = new LinkedList<>(); // 具体的实现类LinkedList,相比于接口,可使用的方法更多
}
2.实现队列的基本操作(进出队列级元素获取)
用链表实现
class Node {
public int val;
public Node next;
public Node(int val) {//构造方法 构造方法主要进行初始化
this.val = val;
}
}
public class MyQueue {
public Node head; //定义一个头节点,尾结点last,标志头和尾
public Node last;
//也即是单链表的尾插法
public void offer(int val) { //添加一个元素进链表队列 offer从 队尾进---》尾插法
Node node = new Node(val); //new 初始化一个node
if (head == null) { //如果是空节点,那么插入之后头尾都是插入的这个节点
head = node;
last =node;
}else {
last.next = node;
last = last.next;
}
}
//出队:删除掉对头的第一个元素
public int poll() {
if (isEmpty()) {
throw new RuntimeException("队列为空");
}
int oldVal =head.val; //删除的元素为当前head的val值
this.head = head.next; //新的head为head.next
return oldVal;
}
public boolean isEmpty() {
return this.head == null;
}
//获取队头的数据 :不需要做删除处理直接是head.val
public int peek() {
if (isEmpty()) {
throw new RuntimeException("队列为空");
}
return head.val;
}
}
面试题1: 使用两个队列实现一个后入先出(LIFO)的栈
并支持普通栈的全部四种操作(push、top、pop 和 empty)
解析:1.先定义两个队列queue1和queue2,并进行初始化
public class QueueToStack {
private Queue<Integer> queue1; //首先定义两个队列
private Queue<Integer> queue2;
public QueueToStack() { //用构造方法对队列进行初始化
queue1 = new LinkedList<>();
queue2 = new LinkedList<>();
}
//入栈:哪个不为空入哪个
public void push(int x) {
if (!queue1.isEmpty()) { //先判断哪个队列不为空,那个不是空就将数据放在哪个
queue1.offer(x);
} else if (!queue2.isEmpty()) {
queue2.offer(x);
}else {
queue1.offer(x); 若都不空,则放入queue1
}
}
//出栈:哪个不为空出哪个
public int pop() {
if (empty()) return -1;
if (!queue1.isEmpty()) {
int size = queue1.size();
for (int i = 0; i < size-1; i++) {
int val = queue1.poll(); //弹出一个元素
queue2.offer(val);
}
return queue1.poll();
}
if (!queue2.isEmpty()) {
int size = queue2.size();
for (int i = 0; i < size-1; i++) {
int val = queue2.poll();
queue1.offer(val);
}
return queue2.poll();
}
return -1;
}
//得到队头元素
public int top() {
if (empty()) return -1;
if (!queue1.isEmpty()) {
int val = -1;
int size = queue1.size();
for (int i = 0; i < size;i++) {
val = queue1.poll();
queue2.offer(val);
}
return val;
}
if (!queue2.isEmpty()) {
int val = -1;
int size = queue2.size();
for (int i = 0; i < size;i++) {
val = queue2.poll();
queue1.offer(val);
}
return val;
}
return -1;
}
public boolean empty() {
return queue1.isEmpty() && queue2.isEmpty();
}
}
面试题2: 2个栈实现队列
队列应当支持一般队列支持的所有操作(push、pop、peek、empty)
public class StackToQueue {
public Stack<Integer> stack1;
public Stack<Integer> stack2;
public StackToQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
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.peek();
}
//获取出栈元素
public int peek() {
if (empty()) return -1;
if (stack2.isEmpty()) {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
public boolean empty() {
return stack1.isEmpty() && stack2.isEmpty();
}
}
面试题4:循环队列实现
循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则 * 并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里, * 一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。 * 但是使用循环队列,我们能使用这些空间去存储新的值。
public class MyCircleQueue {
public int[] elem; //底层是数组,先定义一个数组
public int front; //队头下标
public int rear;//队尾下标
public MyCircleQueue(int k) {
this.elem =new int[k]; //初始化数组大小为k,
}
//入队
public boolean enQueue(int value) {
if(isFull()) return false; //如果满了则不能入队
this.elem[rear] = value;
//rear++
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 = -1;
if (rear == 0) { //当队尾是0下标时,索引为数组长度-1
index = elem.length-1;
}else {
index = rear-1;
}
return elem[index];
}
public boolean isEmpty() {
return front == rear;
}
public boolean isFull() {
if ((this.rear+1) % elem.length == front) {
return true;
}
return false;
}
}