1.队列的概念
只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(FirstIn First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一1端称为队头(Head/Front)
2.特点
先进先出,这个特点我们可以和栈相比较,栈是先进后出
废话不多说,先看队列(Queue)的源码:
public interface Queue<E> extends Collection<E>
普通队列的方法
Queue是普通队列,而队列还有一种更高级的形式,Deque,这是双端队列的形式,双端队列相较于普通队列来说比较复杂,会在后面的内容详细说明
根据队列的方法我们可以知道队列一共有六种方法,但是这六种方法等同于三种方法,放入 移出 查看 ,每种方法基本是相同的,根据使用的场景不同来变换,但是具体的使用都是大同小异的
运行结果如下:
这里插一句,ArrayList和LinkedList的区别?
作为面试官常问的题,也是非常重要的 ,
附带一个链接,可以参考大佬的文章阿里面试官:说一下ArrayList和LinkedList的区别?_沉默王二-CSDN博客
用单链表实现队列
import com.sun.org.apache.bcel.internal.generic.ATHROW;
/**
* 用单链表来实现队列
*/
class Node{
public int val;
public Node next;
public Node(int val){
this.val = this.val;
}
}
public class MyQueue {
public Node head;
public Node last;
/**
*向队列里添加元素(尾插法)
* @param val
*/
public void offer(int val){
Node node = new Node(val);
if(head == null){
head = node;
last = node;
}else {
last.next = node;
last = last.next;
}
}
/**
* 出队
* @return
*/
public int poll(){
if(isEmpty()){
throw new RuntimeException("队列为空");
}
int ret = head.val;
this.head = head.next;
return ret;
}
/**
* 查看队顶元素
* @return
*/
public int peek(){
if(isEmpty()){
throw new RuntimeException("队列为空");
}
return head.val;
}
public boolean isEmpty(){
return this.head == null;
}
}
想用单链表创建一个队列,就必须建立一个last节点,这样才能够是的出队和入队的时间复杂度都为O(1),可以根据上面的代码来查看
*循环队列*
概念:
为充分利用向量空间,克服"假溢出
"现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。这种循环队列可以以单链表
的方式来在实际编程应用中来实现。
题目:
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。
class MyCircularQueue {
public int[] elem;
public int front; //对头下标
public int reer; //队尾下标
public MyCircularQueue(int k) {
this.elem = new int[k+1];
}
/**
入队
*/
public boolean enQueue(int value) {
if(isFull()){
return false;
}else{
this.elem[reer] = value;
reer = (reer + 1)%elem.length;
return true;
}
}
public boolean deQueue() {
if(!isEmpty()){
front = (front + 1)%elem.length; //只要将front++就行了,后面的reer会覆盖掉元素,就等同于删除了一个元素
return true;
}
return false;
}
public int Front() {
if(isEmpty()){
return -1;
}
return elem[front];
}
public int Rear() {
if(isEmpty()){
return -1;
}
int index = 0;
if(reer == 0){
index = elem.length -1;
}else{
index = reer -1;
}
return elem[index];
}
public boolean isEmpty() {
if(front == reer){
return true;
}
return false;
}
public boolean isFull() {
if((reer + 1)%elem.length == front){
return true;
}
return false;
}
}
/**
* Your MyCircularQueue object will be instantiated and called as such:
* MyCircularQueue obj = new MyCircularQueue(k);
* boolean param_1 = obj.enQueue(value);
* boolean param_2 = obj.deQueue();
* int param_3 = obj.Front();
* int param_4 = obj.Rear();
* boolean param_5 = obj.isEmpty();
* boolean param_6 = obj.isFull();
*/
题解:使用数组
数组
思路根据问题描述,该问题使用的数据结构应该是首尾相连的 环。
任何数据结构中都不存在环形结构,但是可以使用一维 数组 模拟,通过操作数组的索引构建一个 虚拟 的环。很多复杂数据结构都可以通过数组实现。
对于一个固定大小的数组,任何位置都可以是队首,只要知道队列长度,就可以根据下面公式计算出队尾位置:
\text{tailIndex} = (\text{headIndex} + \text{count} - 1) \mod \text{capacity}
tailIndex=(headIndex+count−1)modcapacity其中 capacity 是数组长度,count 是队列长度,headIndex 和 tailIndex 分别是队首 head 和队尾 tail 索引。下图展示了使用数组实现循环的队列的例子。
题目:
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
class MyStack {
public Queue<Integer> qu1;
public 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 size = qu1.size();
for(int i = 0; i < size-1; i++){
int val = qu1.poll();
qu2.offer(val);
}
return qu1.poll();
}
if(!qu2.isEmpty()){
int size = qu2.size();
for(int i = 0; i < size-1; i++){
int val = qu2.poll();
qu1.offer(val);
}
return qu2.poll();
}
return -1;
}
public int top() {
if(empty()){
return -1;
}
if(!qu1.isEmpty()){
int size = qu1.size();
int val = 0;
for(int i = 0; i < size; i++){
val = qu1.poll();
qu2.offer(val);
}
return val;
}
if(!qu2.isEmpty()){
int size = qu2.size();
int val = 0;
for(int i = 0; i < size; i++){
val = qu2.poll();
qu1.offer(val);
}
return val;
}
return -1;
}
public boolean empty() {
return qu1.isEmpty() && qu2.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
题目:
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
class MyQueue {
public Stack<Integer> s1;
public 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;
}
int size = s1.size();
if(s2.isEmpty()){
for(int i = 0; i < size; i++ ){
int val = s1.pop();
s2.push(val);
}
}
return s2.pop();
}
public int peek() {
if(empty()){
return -1;
}
if(s2.isEmpty()){
int size = s1.size();
for(int i = 0; i < size; i++ ){
int val = s1.pop();
s2.push(val);
}
}
return s2.peek();
}
public boolean empty() {
return s1.isEmpty() && s2.isEmpty();
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
以上三个oj是基础但也是重中之重,具体的思路可以在代码中查看(狗头) !!!