一、队列基本概念
队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在其一端进行插入操作在其另一端进行删除操作。
队列中允许进行插入操作的一端称为队尾,允许进行删除操作的一端称为队头。队列的插入操作通常称作入队列,队列的删除操作通常称作出队列。下图是一个依次向队列中插入数据元素a0,a1,...,an-1后的示意图,其中,a0是当前队头数据元素,an-1是当前队尾数据元素。
二、队列抽象数据类型
1 数据集合
队列的数据集合可以表示为a0,a1,…,an-1,每个数据元素的数据类型可以是任意的类型。
2 操作集合
(1)入队列append(obj):把数据元素obj插入队尾。
(2)出队列delete():把队头数据元素删除并由函数返回。
(3)取队头数据元素getFront():取队头数据元素并由函数返回。
(4)非空否isEmpty():非空否。若队列非空,则函数返回false,否则函数返回true。
三、顺序队列
顺序队列的存储结构
下图是一个有6个存储空间的顺序队列的动态示意图,图中front指示队头,rear指示队尾。
顺序队列的"假溢出"现象
四、顺序循环队列
假溢出是由于队尾rear的值和队头front的值不能由所定义数组下界值自动转为数组上界值而产生的。因此,解决的方法是把顺序队列所使用的存储空间构造成一个逻辑上首尾相连的循环队列( Circular Queue)。
当rear和front达到maxSize-1后,再加1就自动到0。这样,就不会出现顺序队列数组的头部已空出许多存储空间,但队尾却因数组下标越界而引起溢出的假溢出问题。解决这个问题的方法有三种:
(1)设计一个布尔变量以判断队列的空和满;
添加一个标志位。设标志位为tag,初始时置tag=0;每当入队列操作成功就置tag=1;每当出队列操作成功就置tag=0。则队列空的判断条件为:
rear == front && tag==0
队列满的判断条件为:
rear = = front && tag= =1
(2)少用一个存储空间。
当少用一个存储空间时,以队尾rear加1等于队头 front为队列满的判断条件,即队列满的判断条件此时为:
(rear + 1) % maxSize == front
队列空的判断条件仍然为:
rear = = front
(3)设计一个计数器,统计队列中得元素个数。添加一个计数器。设计数器为count,初始时置count=0;每当入队列操作成功就使count加1;每当出队列操作成功就使count减1。这样,该计数器不仅具有计数功能,而且还具有像标志位一样的标志作用,则此时队列空的判断条件为:
count == 0
队列满的判断条件为:
count > 0 && rear == front
五、顺序循环队列的实现
//队列接口 public interface Queue { //入队 public void append(Object obj) throws Exception; //出队 public Object delete() throws Exception; //获得对头元素 public Object getFront() throws Exception; //判断队列是否为空 public boolean isEmpty(); }
//顺序循环队列实现 public class CicleSequenceQueue implements Queue { //默认队列长度 static final int defaultSize=10; //统计元素个数的计数器 int count; //对头 int front; //队尾 int rear; //队列 Object queue[]; //对列最大长度 int maxSize; public CicleSequenceQueue() { init(defaultSize); } //自定义队列长度 public CicleSequenceQueue(int size) { init(size); } public void init(int size) { this.maxSize=size; front=rear=0; count=0; queue=new Object[maxSize]; } @Override public void append(Object obj) throws Exception { if(count>0&&front==rear) { throw new Exception("队列已满!"); } queue[rear]=obj; rear=(rear+1)%maxSize; count++; } @Override public Object delete() throws Exception { if(isEmpty()) { throw new Exception("队列为空,不能删除!"); } Object obj=queue[front]; front=(front+1)%maxSize; count--; return obj; } @Override public Object getFront() throws Exception { if(isEmpty()) return null; else return queue[front]; } @Override public boolean isEmpty() { return count==0; } }
测试:
public class Main { public static void main(String[] args) throws Exception { CicleSequenceQueue queue=new CicleSequenceQueue(10); queue.append("a"); queue.append("b"); queue.append("c"); queue.append("d"); queue.append("e"); queue.append("f"); queue.append("g"); queue.append("h"); queue.append("i"); queue.append("j"); queue.delete(); queue.delete(); queue.append("k"); while(!queue.isEmpty()) { System.out.println(queue.delete()); } } }
六、用顺序循环队列实现生产者-消费者模式(模拟排队买票和卖票过程)
//卖票窗口 public class WindowQueue { //队列长度 static final int maxSize=10; CicleSequenceQueue queue=new CicleSequenceQueue(maxSize); //统计卖票的数量,一天最多可以卖100张 int num=0; //是否可以继续卖票 boolean isAlived=true; //排队买票 public synchronized void producer() throws Exception{ if(queue.count<maxSize) { queue.append(num++); System.out.println("第"+num+"个人开始排队买票!"); this.notifyAll(); }else { System.out.println("队列已满,请等待!"); this.wait(); } } //卖票 public synchronized void cousumer() throws Exception{ if(queue.count>0) { int temp=Integer.parseInt(queue.delete().toString()); //如果队列以空,并且卖出的数量大于100,则退出 if(queue.isEmpty()&&num>100) { this.isAlived=false; } System.out.println("第"+(temp+1)+"个人买完票离开!"); this.notifyAll(); }else { System.out.println("队列已空,请等待!"); this.wait(); } } }//买票者 public class Producer implements Runnable { WindowQueue queue; public Producer(WindowQueue queue) { this.queue=queue; } @Override public void run() { while(queue.num<100) { try { queue.producer(); } catch (Exception e) { e.printStackTrace(); } } } }//卖票者 public class Consumer implements Runnable { WindowQueue queue; public Consumer(WindowQueue queue) { this.queue=queue; } @Override public void run() { while(queue.isAlived) { try { queue.cousumer(); } catch (Exception e) { e.printStackTrace(); } } } }//测试 public class Test { public static void main(String[] args) { WindowQueue queue=new WindowQueue(); Producer producer=new Producer(queue); Consumer consumer=new Consumer(queue); new Thread(producer).start(); new Thread(consumer).start(); } }
结果: