一、队列的介绍
队列(Queue)是一种特殊的线性表,限定只能在表的一端进行插入(队尾),而在另一端进行删除操作(队头),特点是“先进先出”(FIFO)。队列的使用方式为通过Queue接口的实现类来创建对象,比较常见的实现类有LinkedList、ArrayDeque、PriorityQueue,通常使用LinkedList来创建队列新对象即可,样例代码为Queue<String> queue = new LinkedList<> () ;这里穿插一些个人理解,通过LinkedList我们可以创建Queue对象,也可以创建LinkedList对象,即LinkedList<String> queue = new LinkedList<> ();但是具体在调用方法时使用的是各自(队列和链表)的方法,这说明还是要看创建的是什么类型的对象,即使这里二者都是通过相同的类来创建得到的对象。
队列的基本操作包括压入元素(添加):offer()、弹出元素(删除):poll()、获取队头元素(不删除):peek()。使用以上三种方法即可。样例代码分别为:queue.offer("12");queue.poll();String str = queue.peek();。
二、Java实现自定义队列
自定义一个队列,队列的底层采用数组。然后使用front和end来代表队尾插入位置的指针和队头弹出元素的指针,然后使用nItems来代表当前队列中元素的个数。 然后创建insert、remove方法来压入数据和弹出数据,peekFront方法来得到队列队头的数据,isEmpty来判定队列是否为空,isFull方法来判断队列是否已满。如下代码表示的是循环队列的实现,可以得到正确的测试结果。
1 循环队列
为了避免队列不满,却不能插入新数据项的问题,可以让队头队尾的指针绕回到数组开始的位置,这就是循环队列,也称为缓冲环。
package com.ms.MyQueue;
public class MyQueue {
//存入队列数据的数组,自定义队列的底层是数组,这里先用int数组
private int[] queue;
//使用front来表示弹出数据的指针,默认为0;
private int front;
//使用end来表示压入数据的指针,默认为-1,每次使用时先加1;
private int end;
//使用nItems来表示当前队列中元素的个数
private int nItems;
//构造函数
public MyQueue(int length){
queue = new int[length];
front = 0;
end = -1;
nItems = 0;
}
//压入数据的方法
public void insert(int data){
//先对end进行一个判定,如果到了底层数组的最后一位,那么就让它让返回至首位
if(end==queue.length-1){
end=-1;
}
end++;
queue[end] = data;
nItems++;
if (nItems>queue.length){
nItems= queue.length;
}
}
//弹出数据的方法
public int remove(){
//根据已存放数据的个数来首先判断
if(nItems==0){
return 0 ;
}
//找媒介暂时存放数组数据
int temp = queue[front];
queue[front]=0;
//front+1之前维护一下,说的是当front是底层数组最后一位时的情况
if (front== queue.length-1){
front=-1;
}
//取完了数据之后,将front加1
front++;
return temp;
}
//获取队头处的数据
public int peekFront(){
return queue[front];
}
//判断队列是否为空
public boolean isEmpty(){
return nItems==0;
}
//判断队列是否已满
public boolean isFull(){
return nItems== queue.length;
}
//清空队列
public void clear(){
queue = new int[queue.length];
}
public static void main(String[] args) {
MyQueue que = new MyQueue(6);
que.insert(1);
que.insert(9);
que.insert(9);
que.insert(7);
que.insert(0);
que.insert(8);
System.out.println(que.remove());
System.out.println(que.isFull());
//System.out.println(que.remove());
que.remove();
que.remove();
que.remove();
que.remove();
que.remove();
System.out.println(que.front);
}
}
2 双端队列
所谓双端队列,就是一个两端都可以进行数据插入和移出操作的队列。它综合提供了栈和队列的功能,但并不常见。
3 优先级队列
优先级队列就是数据项按照关键字排好顺序的队列,即队列中的数据按顺序排列。
思考一下可以想到,优先级队列相比于普通(循环)队列不同之处应该是在插入时要进行排列。因为涉及排序,就尽量不循环,所以插入队尾指针end和弹出队头指针front就先不要了(nItems记录了当前队列里存放的元素个数,也相当于是索引),并且在创建对象的时候要把length设置的大一些就可以了。然后在插入数据的过程中我们使用到了 插入法排序,从而使得队列中的数据升序或者降序。以下是优先级队列的代码,运行后可以得到正确结果
package com.ms.MyQueue;
import javafx.scene.layout.Priority;
public class PriorityQueue {
//优先级队列的底层是数组,这里先默认类型是整数
private int[] queue;
//使用nItems来表示当前队列中元素的个数
private int nItems;
//无参构造函数
public PriorityQueue(){}
//有参构造函数
public PriorityQueue(int length){
queue = new int[length];
nItems = 0;
}
//插入方法
public void insert(int data){
//队列中没有数据的话就直接加入
if(nItems==0){
queue[nItems]=data;
}else{
//使用插入法进行排序,把数据放入合适的位置(由大到小的顺序,这样的话按照nItems取数据时就按照由小到大的顺序取)
int i = 0;
for ( i=nItems-1;i>=0;i--){
if (data>queue[i]){
queue[i+1]=queue[i];
}else{
break;
}
}
queue[i+1]=data;
}
nItems++;
}
//弹出方法
public int remove(){
//取得时候先取最小的
//先减小
nItems--;
int tep = queue[nItems];
return tep;
}
//取队头值
public int peekfront(){
return queue[nItems-1];
}
public boolean isEmpty(){
return nItems==0;
}
public boolean isFull(){
return nItems== queue.length;
}
public static void main(String[] args) {
PriorityQueue que = new PriorityQueue(10);
que.insert(2);
que.insert(5);
que.insert(4);
que.insert(6);
que.insert(1);
que.insert(3);
System.out.println(que.remove());
System.out.println(que.remove());
System.out.println(que.remove());
System.out.println(que.remove());
System.out.println(que.remove());
System.out.println(que.remove());
}
}