数据存储——队列(Queue)
本章节的源代码位于gitee上,想要下载的请点击数据存储——队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。
相关的队列执行原理就如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZpMmwFD0-1602055132967)(C:\Users\C3H2\AppData\Roaming\Typora\typora-user-images\image-20201007141952158.png)]
接下来我们就来编写一个队列。队列的编写依然采用链表的方式。该类主要编写如下几个方法。
empty() 如果队列为空返回true,否则返回false
size() 返回队列中元素的个数
pop() 删除队列首元素但不返回其值
front() 返回队首元素的值,但不删除该元素
push() 在队尾压入新元素
back() 返回队列尾元素的值,但不删除该元素
队列的创建与添加数据
队列的创建于栈是十分类似的,需要注意的是,栈可以采用单指针进行数据操作,而队列需要使用双指针,一个指针在队首,一个指针在队尾,队尾指针负责压入数据,队首指针负责弹出数据。
interface IQueue<T>{
void push(T data);//压入数据
}
class QueueImpl<T> implements IQueue<T>{
private Node hand;//队首
private Node next;//队尾
private int size;//队列长度
@Override
public void push(T data) {
Node node = new Node(data);
if(this.hand==null){
this.hand = node;
this.next = node;
} else {
this.next.last = node;
this.next = this.next.last;
}
this.size ++ ;
}
private class Node<T>{//链表结点
private T data;
private Node last;//保存下一个结点
private Node(T data){
this.data = data;
}
}
}
在队列中压入数据其实和链表是完全一样的,如果你对Java中的LinkedList类有了解的话就可以知道它也是继承了一个名为Queue的接口,该接口就是Java定义的一个队列操作接口。所以这里就不细说队列的压入数据操作了
队列数据操作(弹出元素、队列是否为空)
在操作队列数据的时候需要注意的是,移动的是队首的指针,队尾的指针是不需要移动的,只有在压入数据的时候才需要移动队首的指针。接下来的操作和单链表操作类似,这里就快速过一下。
empty() 如果队列为空返回true,否则返回false
size() 返回队列中元素的个数
pop() 删除队列首元素但不返回其值
front() 返回队首元素的值,但不删除该元素
back() 返回队列尾元素的值,但不删除该元素
相关代码为:
@Override
public boolean empty() {
return this.hand!=null;
//return this.size!=0;//两种方法都可以
}
@Override
public int size() {
return this.size;
}
@Override
public void pop() {
this.size -- ;
this.hand = this.hand.last;//指针移动
}
@Override
public T front() {
return (T) this.hand.data;//返回数据
}
@Override
public T back() {
return (T) this.next.data;//返回队尾数据
}
代码测试
public class SimulateQueueDemo {
public static void main(String[] args) {
IQueue<String> queue = new QueueImpl<>();
queue.push("A");
queue.push("B");
queue.push("C");
System.out.println("队首数据为:"+queue.front());
System.out.println("队尾数据为:"+queue.back());
while(queue.empty()){
System.out.print("弹出数据为:"+queue.front());
queue.pop();
System.out.println(",队列中数据剩余:"+queue.size());
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kTrSbTYI-1602055132977)(C:\Users\C3H2\AppData\Roaming\Typora\typora-user-images\image-20201007151618514.png)]
总结
队列的出现是为了解决资源紧张的设计问题,因为资源不足,所以只能后进行排队依次处理,队列的出现解决的是数据缓存的问题。
全部代码如下,也可以去gitee上下载
package cn.tansanqinger.linked;
interface IQueue<T>{
void push(T data);//压入数据
boolean empty();//如果队列为空返回true,否则返回false
int size();//返回队列中元素的个数
void pop();//删除队列首元素但不返回其值
T front();//返回队首元素的值,但不删除该元素
T back();//返回队列尾元素的值,但不删除该元素
}
class QueueImpl<T> implements IQueue<T>{
private Node hand;//队首
private Node next;//队尾
private int size;//队列长度
@Override
public void push(T data) {
Node node = new Node(data);
if(this.hand==null){
this.hand = node;
this.next = node;
} else {
this.next.last = node;
this.next = this.next.last;
}
this.size ++ ;
}
@Override
public boolean empty() {
return this.hand!=null;
//return this.size!=0;//两种方法都可以
}
@Override
public int size() {
return this.size;
}
@Override
public void pop() {
this.size -- ;
this.hand = this.hand.last;//指针移动
}
@Override
public T front() {
return (T) this.hand.data;//返回数据
}
@Override
public T back() {
return (T) this.next.data;//返回队尾数据
}
private class Node<T>{//链表结点
private T data;
private Node last;//保存下一个结点
private Node(T data){
this.data = data;
}
}
}
public class SimulateQueueDemo {
public static void main(String[] args) {
IQueue<String> queue = new QueueImpl<>();
queue.push("A");
queue.push("B");
queue.push("C");
System.out.println("队首数据为:"+queue.front());
System.out.println("队尾数据为:"+queue.back());
while(queue.empty()){
System.out.print("弹出数据为:"+queue.front());
queue.pop();
System.out.println(",队列中数据剩余:"+queue.size());
}
}
}
其他数据存储知识点