什么是队列:

队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。

队列的基本操作:

enqueue(Object obj):入队操作

dequeue():出队操作

循环队列:

由于队列如果做成数组的形式,为了保证出队列的时间复杂度为O(1),所以不能将数组中的元素进行移动(如果移动,时间复杂度就变为了O(n))。因此前面出栈之后的元素空间就会被浪费。所以我们将数组的头尾进行相接,这样就形成了循环,我们称这样的队列为循环队列。结构图如下所示:

java定义原生的队列 java队列怎么定义_java语言队列实现

判断队列为空:front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样的front等于rear的时候,此时队列不是还剩下一个元素,而是为空队列。所以空队列的判断为front == rear。

判断队列为满:当队列为满时,我们修改其条件,保存一个元素空间。也就是说,队列为满时,数组中还有一个空闲单元。(这就是为什么后面的代码中数组的大小为5,却最多可以插入4个元素)所以满队列的判断为(rear+1)%QueueSize == front。 代码示例如下:

package queue;
/**
* 数组中只存储数组大小-1个元素,
* 保证rear转一圈之后不会和head相等,
* 也就是队列满的时候,rear+1=head,
* 中间刚好空一个元素。
* 当rear=head的时候,一定是队列空了。
*
*/
public class QueueArray {
private Object[] objs;
private int front;
private int rear;
public QueueArray(){
this(10);
}
public QueueArray(int size){
objs = new Object[size];
front = 0;
rear = 0;
}
public boolean enqueue(Object obj){
//判断队列是否为满
if((rear+1)%objs.length == front){
return false;
}
objs[rear] = obj;
rear = (rear+1)%objs.length;
return true;
}
public Object dequeue(){
//判断队列是否为空
if(rear == front){
return null;
}
Object obj = objs[front];
front = (front+1)%objs.length;
return obj;
}
public void traverse(){
while(front != rear){
System.out.print(objs[front]+" ");
front = ((front+1)%objs.length);
}
}
public static void main(String[] args) {
QueueArray q = new QueueArray(5);
q.enqueue("A");
q.enqueue("B");
q.enqueue("C");
System.out.println("删除的元素为:"+q.dequeue());
q.enqueue("F");
System.out.println("删除的元素为:"+q.dequeue());
q.traverse();
}
}

队列的链式存储方式:

入队与出队结构图:

java定义原生的队列 java队列怎么定义_java语言队列实现

              

java定义原生的队列 java队列怎么定义_java语言队列实现

入队:将新节点作为原来节点的后继,再将新节点设置为尾节点。this.rear.next = newNode;    this.rear = newNode;

出队:将需要删除节点的后继直接赋值给头结点的后继即可。this.front.next = node.next;

具体代码示例如下:

package queue;
public class LinkQueue {
private class Node{
private T data;
private Node next;
public Node(){}
}
private Node front;
private Node rear;
private int count;//队列中元素的数量
public LinkQueue(){
Node p = new Node();
p.data = null;
p.next = null;
front = rear = p;
}
//使用尾插法插入数据
public void equeue(T item){
Node newNode = new Node();
newNode.data = item;
newNode.next = null;
this.rear.next = newNode;
this.rear = newNode;
count++;
}
public T dequeue() throws Exception{
if(isEmpty()){
throw new Exception("队列为空!");
}
T obj;
Node node = this.front.next;
obj = node.data;
this.front.next = node.next;
if(rear == node){
rear = front;
}
count --;
return obj;
}
public int size(){
return count;
}
public boolean isEmpty(){
return front == rear;
}
public void traverse(){
Node current = front.next;
while(current != null){
System.out.println(current.data);
current = current.next;
}
}
public static void main(String[] args) throws Exception {
LinkQueue lq = new LinkQueue();
lq.equeue("A");
lq.equeue("B");
lq.equeue("C");
lq.equeue("D");
lq.traverse();
System.out.println("队列的长度为:"+lq.size());
System.out.println("删除的元素为:"+lq.dequeue());
lq.traverse();
}
}

无论是循环队列还是链式队列,从时间上看基本都是常数的时间,即时间复杂度都为O(1)。不过循环队列是事先申请好了空间,使用期间不释放。而对于链式队列,每次申请或释放节点也会存在一定的开销,如果入队或出队频繁,则还是存在细微的差异,但是链式队列在空间上更加灵活。所以,在可以确定队列长度最大值的情况下,建议用循环队列,如果无法预估队列的长度,则使用链队列。