队列(Queue)
今天给大家带来的是一篇关于数据结构中队列的知识,主要讲解了队列,以及其相关api和队列的简单实现,以及三道经典的OJ题,包括用队列实现栈、用栈实现队列、实现一个最小栈。
话不多说,我们直接进入正题!
队列概念
队列是我们常见的一种数据结构,它只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。队列具有先进先出的特点,根据其元素的进出位置,将其分为:
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端成为队头
队列的具体表现如图所示:
对于队列说在集合框架中是一个接口,但是从如下的图可以看出,队列的底层实现是一个双向链表来进行实现的。
因此我们就采用双向链表对于队列的关键API进行模拟实现
模拟实现队列
对于队列其实现的关键API参数如下:
API | 实现的功能 |
---|---|
boolean offer(E e) | 入队列 |
E poll() | 出队列 |
peek() | 获取队头元素 |
int size() | 获取队列中有效元素个数 |
boolean isEmpty() | 检测队列是否为空 |
实现的代码如下:
public class MyQueue2<E>{
//使用双向链表作为底层实现的原理
public static class ListNode<E>{
ListNode<E> next;
ListNode<E> prev;
E value;
ListNode(E value){
this.value=value;
}
}
ListNode<E> first;
ListNode<E> last;
int size=0;
//入队列为队尾 出队列为队头
public void offer(E e){
ListNode<E> newNode=new ListNode<>(e);
if (first==null){
first=newNode;
}else {
last.next=newNode;
newNode.prev=last;
}
last=newNode;
size++;
}
public E poll(){
//判断队列是否为空
//队列中只有一个元素
//队列中有多个元素
E value=null;
if (first==null){
return null;
}else if (first==last){
value=first.value;
last=null;
first=null;
}else {
//有其他的节点
value=first.value;
first=first.next;
//前一个节点的下一个下标地址为空
first.prev.next=null;
//指向前一个的地址为空
first.prev=null;
}
size--;
return value;
}
//获取队列的头元素
public E peek(){
if (first==null){
return null;
}
return first.value;
}
//长度
public int size(){
return size;
}
public boolean isEmpty(){
return first==null;
}
@Override
public String toString() {
String s="[";
ListNode<E> cur=first;
while (cur!=null){
if (cur.next!=null){
s+=cur.value+" ";
}else {
s+=cur.value;
}
cur=cur.next;
}
s+="]";
return s;
}
public static void main(String[] args) {
MyQueue2<Integer> myQueue2=new MyQueue2<>();
myQueue2.offer(1);
myQueue2.offer(2);
myQueue2.offer(3);
myQueue2.offer(4);
myQueue2.offer(5);
myQueue2.offer(6);
myQueue2.offer(7);
myQueue2.offer(8);
myQueue2.offer(9);
System.out.println(myQueue2);
int m=myQueue2.poll();
System.out.println(m);
System.out.println(myQueue2);
System.out.println(myQueue2.size());
System.out.println(myQueue2.peek());
System.out.println(myQueue2.isEmpty());
}
}
队列的相关OJ题
模拟实现循环队列
具体要求:
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满
实现分析:
构造器:根据输入的长度来实现构造即可
获取队首元素:调用方法判断是否为空,直接输出数组的第一个元素即可
获取队尾元素:调用方法判断是否为空,输出数组的第一个元素,此时需要判断是否是数组存在越界的情况,利用循环下标的方式将数组的元素进行获取输出。
向循环链队列插入一个元素:调用方法判断队列是否是一个满的状态,如果是不满的状态就向队尾进行插入,同时将插入的数据数量进行加一的操作。最后判断插入后的长度是否等于构建的长度,如果等于将最后一个元素重新赋值为0,
向循环链队列删除一个元素:调用方法判断队列是否为空,如果不为空则就进行将删除操作,同时下标向后移动,但是需要判断是否是下标越界的情况。
检查循环队列是否为空:通过计数来判断
检查循环队列是否已满:通过计数和构造的长度是否一样来判断
实现的代码如下:
//循环队列
public class MyCircularQueue {
//利用循环数组下标来进行实现该操作
int[] array; //循环队列的容器
int first=0; //循环队列开始的地方
int end=0;//末尾的地方
int n; //表示循环队列的长度
int size=0;
public MyCircularQueue(int k) {
array=new int[k];
n=k;
}
//插入元素,如果成功插入则返回真
public boolean enQueue(int value) {
if (isFull()){
return false;
}
//从队尾进入队列中
array[end]=value;
end++;
size++;
if (end==n){
end=0;
}
return true;
}
public boolean deQueue() {
if (isEmpty()){
return false;
}
first++;
//防止在出对队列的时候崩溃
first%=n;
size--;
return true;
}
//获取队头元素
public int Front() {
if (isEmpty()){
return -1;
}
return array[first];
}
//获取队尾元素
public int Rear() {
if (isEmpty()){
return -1;
}
//防止产生数组越界的情况
return array[(end-1+n)%n];
}
//是否为空
public boolean isEmpty() {
return size==0;
}
//是否已满
public boolean isFull() {
//通过长度来进行判断
return size==array.length;
}
}
用队列实现栈的操作
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
实现思路:将最新插入的元素放在一个空队列中,然后将之前的元素更新到当前的队列中,就可保证在当前的队列出来的元素满足后进先出(栈的特性)。
具体实现代码:
class MyStack {
public Queue<Integer> q1;
public Queue<Integer> q2;
public MyStack() {
q1=new LinkedList<>();
q2=new LinkedList<>();
}
public void push(int x) {
//用一个来接收
q1.offer(x);
//用将当前的这个数据存放在q2中 保证q1始终为空
while (!q2.isEmpty()) {
q1.offer(q2.poll());
}
//现在所有的元素存放在q1当中 q1和q2进行交换
Queue tmp=q1;
q1=q2;
q2=tmp;
//此时q2中为所有的元素,且q1为空 q2的队头元素为最新插入的元素。
}
//出队列
public int pop() {
return q2.poll();
}
public int top() {
return q2.peek();
}
public boolean empty() {
return q2.isEmpty();
}
}
用栈来实现队列
要求:
实现 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);
s2.push(s1.pop());
//现在s2栈是满的 s1为空
}
public int pop() {
while (!s2.isEmpty()){
//交换元素的出栈顺序
s1.push(s2.pop());
}
//获取最早的栈元素
int ret=s1.pop();
//重现调整放入顺序
while (!s1.isEmpty()){
s2.push(s1.pop());
}
//取出最早的元素
return ret;
}
public int peek() {
while (!s2.isEmpty()){
//交换元素的出栈顺序
s1.push(s2.pop());
}
//获取最早的栈元素
int ret=s1.peek();
//重现调整放入顺序
while (!s1.isEmpty()){
s2.push(s1.pop());
}
//取出最早的元素
return ret;
}
public boolean empty() {
return s2.isEmpty();
}
}
最小栈的实现
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
解题思路:
用一个栈来存储数据,另一个栈来存储最小的数据,最后更新和输出最小的数据即可。
实现代码:
public class MyMinStack {
public Stack<Integer> s1;
public Stack<Integer> s2;
public MyMinStack() {
s1=new Stack<>();
s2=new Stack<>();
}
public void push(int val) {
if(s2.empty() || val <= s2.peek()){
s2.push(val);
}
s1.push(val);
}
public void pop() {
if(s1.peek() == s2.peek()){
s2.pop();
}
s1.pop();
}
public int top() {
return s1.peek();
}
public int getMin() {
return s2.peek();
}
}
以上就是今天队列相关的知识了。谢谢大家的关注!