(八)隊列以及順序循環隊列的應用

隊列基本概念

隊列(簡稱作隊,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。

隊列抽象數據類型的Java接口定義如下:

public interface Queue{

  public voidappend(Object obj) throws Exception;

  public Object delete()throws Exception;

  public ObjectgetFront() throws Exception;

  public booleanisEmpty();

順序隊列

順序隊列的存儲結構
下圖是一個有6個存儲空間的順序隊列的動態示意圖,圖中front指示隊頭,rear指示隊尾。
順序隊列的"假溢出"現象
假溢出是由於隊尾rear的值和隊頭front的值不能由所定義數組下界值自動轉爲數組上界值而產生的。因此,解決的方法是把順序隊列所使用的存儲空間構造成一個邏輯上首尾相連的循環隊列( Circular Queue)。
 當rear和front達到maxSize-1後,再加1就自動到0。這樣,就不會出現順序隊列數組的頭部已空出許多存儲空間,但隊尾卻因數組下標越界而引起溢出的假溢出問題。
解決這個問題的方法有三種:

(1)設計一個布爾變量以判斷隊列的空和滿;

(2)少用一個存儲空間。

(3)設計一個計數器,統計隊列中得元素個數。

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 CircleSequenceQueue implements Queue{
    
	static final int defaultSize = 10; //默認隊列的長度
	int front;  //隊頭
	int rear;   //隊尾
	int count;  //統計元素個數的計數器
	int maxSize; //隊的最大長度
	Object[] queue;  //隊列
	
	public CircleSequenceQueue()
	{
		init(defaultSize);
	}
	
	public CircleSequenceQueue(int size)
	{
	    init(size);	
	}
	
	public void init(int size)
	{
		maxSize = size;
		front=rear=0;
		count=0;
		queue = new Object[size];
	}
	
	@Override
	public void append(Object obj) throws Exception {
		// TODO Auto-generated method stub
		if(count>0&&front==rear)
		{
			throw new Exception("隊列已滿!");
		}
		queue[rear]=obj;
		rear=(rear+1)%maxSize;
		count++;
	}

	@Override
	public Object delete() throws Exception {
		// TODO Auto-generated method stub
		if(isEmpty())
		{
			throw new Exception("隊列爲空!");
		}
		Object obj = queue[front];
		front = (front+1)%maxSize;
		count--;
		return obj;
	}

	@Override
	public Object getFront() throws Exception {
		// TODO Auto-generated method stub
		if(!isEmpty())
		{
		   return queue[front];	
		}
		else
		{
		  return null;
		}
	}

	@Override
	public boolean isEmpty() {
		// TODO Auto-generated method stub
		return count==0;
	}

}

循環隊列應用

實例:使用順序循環隊列和多線程實現一個排隊買票的例子。
生產者(等候買票)
消費者(買票離開)
//賣票者
public class Consumer implements Runnable {
  
	WindowQueue queue ;
    
	public Consumer(WindowQueue queue)
	{
		this.queue = queue;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
	   while(queue.isAlive)
	   {
		   try
		   {
			   queue.consumer();
		   }
		   catch(Exception ex)
		   {
			   ex.printStackTrace();
		   }
	   }
	}
	
}

//買票者
public class Producer implements Runnable {
   
	WindowQueue queue ;
	public Producer(WindowQueue queue)
	{
		this.queue = queue;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(queue.num<100)
		{
			try
			{
			   queue.producer();
			}
			catch(Exception ex)
			{
				ex.printStackTrace();
			}
		}
	}
	
}

//賣票窗口
public class WindowQueue {
  
  //賣票的隊列	
  int maxSize =10;
  CircleSequenceQueue queue = new CircleSequenceQueue(maxSize);
  int num=0; //統計賣票的數量,一天最多賣100張票。
  boolean isAlive = true; //判斷是否繼續賣票。
  
  //排隊買票
  public synchronized void producer() throws Exception
  {
	if(queue.count < maxSize)
	{
		queue.append(num++); //等待買票的數量加1
		System.out.println("第"+num+"個客戶排隊等待買票!");
		this.notifyAll();//喚醒賣票的線程
	}
	else
	{
		try
		{
			System.out.println("隊列已滿...請等待!");
			this.wait();//隊列滿時,排隊買票線程等待。
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
		}
	}
  }
  
  //賣票
  public synchronized void consumer() throws Exception
  {
	if(queue.count > 0)
	{
		Object obj = queue.delete();
		int temp = Integer.parseInt(obj.toString());
		System.out.println("第"+(temp+1)+"個客戶買到票離開隊列!");
		//如果當前隊列爲空,並且賣出票的數量大於等於100,說明賣票結束
		if(queue.isEmpty()&&this.num>=100)
		{
			this.isAlive = false;
		}
		this.notifyAll(); //喚醒排隊買票的線程。
	}
	else
	{
		try
		{
			System.out.println("隊列已空...請等待!");
			this.wait();//隊列空時,賣票線程等待。
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
		}
	}
  }
}


public class Test {


	/**
	 * @param args
	 */
	public static void main(String[] args)throws Exception {
		WindowQueue queue = new WindowQueue();
		
		Producer p = new Producer(queue);//注意一定要傳同一個窗口對象
		Consumer c = new Consumer(queue);
		
		//排隊買票線程
		Thread pThread = new Thread(p);
		//賣票線程
		Thread cThread = new Thread(c);
		
		pThread.start(); //開始排隊買票
		cThread.start();  //開始賣票
	}


}

發佈了31 篇原創文章 · 獲贊 3 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章