标签:
什么是队列:
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
队列的基本操作:
enqueue(Object obj):入队操作
dequeue():出队操作
循环队列:
由于队列如果做成数组的形式,为了保证出队列的时间复杂度为O(1),所以不能将数组中的元素进行移动(如果移动,时间复杂度就变为了O(n))。因此前面出栈之后的元素空间就会被浪费。所以我们将数组的头尾进行相接,这样就形成了循环,我们称这样的队列为循环队列。结构图如下所示:
判断队列为空: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(); } }队列的链式存储方式:
入队与出队结构图:
入队:将新节点作为原来节点的后继,再将新节点设置为尾节点。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)。不过循环队列是事先申请好了空间,使用期间不释放。而对于链式队列,每次申请或释放节点也会存在一定的开销,如果入队或出队频繁,则还是存在细微的差异,但是链式队列在空间上更加灵活。所以,在可以确定队列长度最大值的情况下,建议用循环队列,如果无法预估队列的长度,则使用链队列。
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/u013799929/article/details/47975849