java语言实现队列

什么是队列:

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

队列的基本操作:

enqueue(Object obj):入队操作

dequeue():出队操作

循环队列:

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

java语言实现队列_第1张图片

判断队列为空: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语言实现队列_第2张图片              

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

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

具体代码示例如下:     

package queue;

public class LinkQueue<T> {
	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<String> lq = new LinkQueue<String>();
		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)。不过循环队列是事先申请好了空间,使用期间不释放。而对于链式队列,每次申请或释放节点也会存在一定的开销,如果入队或出队频繁,则还是存在细微的差异,但是链式队列在空间上更加灵活。所以,在可以确定队列长度最大值的情况下,建议用循环队列,如果无法预估队列的长度,则使用链队列。

你可能感兴趣的:(java语言实现队列)