第二章 隊列

1 基本概念

隊列是一種先進先出(First in First Out)的線性表,簡稱FIFO。隊列中,只容許在一端進行插入操做,而在另外一端進行刪除操做。容許插入的一端稱爲隊尾,容許刪除的一端稱爲隊頭。java

以下圖所示,假設隊列q=(a1,a2,…,an),那麼a1就是隊頭元素,而an是隊尾元素。這樣刪除元素時,老是從a1開始,而插入時,老是在隊列尾部插入。後端

1507784-20190716140330407-1176335956

2 數組模擬簡單隊列

 隊列自己是有序列表,若使用數組來存儲隊列的數據,則隊列數組的聲明以下圖所示,其中 maxSize是該隊列的最大容量。由於隊列的輸出、輸入是分別從先後端來處理,所以須要兩個變量front及 rear分別記錄隊列先後端的下標,front 會隨着數據出隊列而改變,而rear則是隨着數據入隊列而改變,以下圖所示:數組

image-20200824233209504

上圖中,變量front與rear的初始值均爲0,這代表front與rear相等時,隊列爲空。當rear==MaxSize成立時,隊列爲滿。須要注意的是,front指向隊列頭部元素位置,而rear指向隊列尾部後一個元素的位置。數據結構

簡單隊列的java實現oop

package com.victor.queue;
import java.util.Scanner;

//數組模擬簡單隊列
public class ArrayQueueDemo {

	public static void main(String[] args) {
		ArrayQueue aq = new ArrayQueue(3);
		char key = ' '; //接收用戶輸入
		Scanner scanner = new Scanner(System.in);
		boolean loop = true;
		//輸出一個菜單欄
		while(loop){
			System.out.println("s(show): 打印隊列");
			System.out.println("a(add): 入隊列");
			System.out.println("g(get): 出隊列");
			System.out.println("h(head): 打印隊頭");
			System.out.println("e(exit): 退出程序");
			key = scanner.next().charAt(0);
			switch (key) {
			case 's':
				aq.showQueue();
				break;
			case 'a': //入隊列
				try {
					System.out.println("請輸入一個整數");
					int value = scanner.nextInt();
					aq.addQueue(value);  //最好判斷一下value是否是整數
				} catch (Exception e) {
					// TODO: handle exception
					System.out.println(e.getMessage());
				}
				break;
			case 'g': //出隊列
				try {
					int res = aq.getQueue();
					System.out.printf("出隊列的整數爲%d\n", res);
				} catch (Exception e) {
					// TODO: handle exception
					System.out.println(e.getMessage()); //輸出getQueue()方法中定義好的異常信息
				}
				break;
			case 'h': //打印隊頭
				try {
					int res = aq.headQueue();
					System.out.printf("隊列頭的整數爲%d\n", res);
				} catch (Exception e) {
					// TODO: handle exception
					System.out.println(e.getMessage());
				}
				break;
			case 'e': //退出
				scanner.close();
				loop = false;
				break;
			default:
				break;
			}
		}
		System.out.println("程序退出");
	}
}


 //使用數組模擬隊列,ArrayQueue類
class ArrayQueue{
	private int maxSize; //數組的最大容量
	private int front;   //隊列頭
	private int rear;   //隊列尾,指向隊列尾部後一個元素的位置
	private int[] arr;  //該數組用於存放數據,模擬隊列
	
	//隊列的構造方法
	public ArrayQueue(int arrMaxSize){
		maxSize = arrMaxSize;
		arr = new int[maxSize];
		front = 0;  //隊列頭部
		rear = 0;  //隊列尾部,指向隊列尾部後一個元素的位置,front=rear時隊列爲空
	}
	
	//判斷隊列是否爲滿隊列
	public boolean isFull(){
		return rear == maxSize;
	}
	
	//判斷隊列是否爲空隊列
	public boolean isEmpty(){
		return rear == front;
	}
	
	//入隊列
	public void addQueue(int n){
		if(isFull()){
			throw new RuntimeException("隊列滿,不能添加數據了");
		}
		arr[rear] = n;
		rear++; //rear後移
	}
	
	//出隊列
	public int getQueue(){
		if(isEmpty()){
			//拋出異常
			throw new RuntimeException("隊列空,不能出隊列了");
		}
		int value = arr[front];
		front++; //front後移
		return value;

	}
	
	//打印隊列
	public void showQueue(){
		if(isEmpty()){
			System.out.println("隊列空");
			return;
		}
		// 打印隊列中的元素,不是打印數組中的全部元素
		for (int i = front; i < rear; i++){
			System.out.printf("arr[%d]=%d\n",i, arr[i]); //格式化輸出
		}
	}
		
	
	//打印隊頭,不是出隊列
	public int headQueue(){
		if (isEmpty()){
			//拋出異常
			throw new RuntimeException("隊列空,不能打印頭");
		}
		return arr[front];  
	}
}

3 數組模擬循環隊列

 以下圖a所示,當front指向下標爲2的數組位置,rear指向下標爲4的數組位置時,若向隊列尾部再添加一個元素\(a_{5}\),則rear指針會越界,以下圖b所示。但此時隊列前部仍然有空間能夠存儲,因此簡單隊列的弊端就是不能充分利用數組空間。優化

image-20200825173213969

圖a

image-20200825172934389

圖b

爲了解決數組空間不夠用的辦法就是後面滿了,就再從頭開始,也就是頭尾相接的循環。咱們把隊列的這種頭尾相接的順序存儲結構稱爲循環隊列。好比上圖b中,咱們把rear置爲0便可解決問題。
 咱們只需對前面的數組模擬簡單隊列進行優化,便可充分利用數組空間。 所以將數組看作是一個環形的,經過對front和rear指針取模實現指針循環。如下圖爲例進行說明:spa

image-20200824233209504

變化以下:指針

  • front變量:指向隊列的第一個元素,初始值爲0。移動狀況爲front = (front + 1)% maxSizecode

  • rear變量:指向隊列的最後一個元素的後一個位置,由於但願空出一個空間作爲約定,rear的初始值爲0。移動狀況爲rear = (rear + 1)% maxSizeblog

  • 當隊列滿時,條件是(rear +1)% maxSize == front成立。由於空出了一個位置,故須要rear+1;

  • 當隊列爲空時,rear == front成立;

  • 隊列中有效數據的個數:這裏有兩種狀況,分別討論:

(1)當front < rear 時,以下圖,隊列中的有效數據個數 = rear - front,這裏rear-front = 2。

image-20200825173213969

(2)當front > rear 時,以下圖,隊列中的有效數據個數=front右邊的數據個數+rear左邊的數據個數,又有:

  • front右邊的數據個數 = maxSize - front,這裏maxSize - front = 3,maxSzie爲數組的最大容量;
  • rear左邊的數據個數 = rear - 0 = rear,這裏爲0;

因此隊列中的有效數據個數 = rear - front + maxSize

image-20200825204541913

綜合兩種狀況可得:

隊列中的有效數據個數 = (rear - front + maxSize) % maxSize

循環隊列的java實現

package com.victor.queue;
import java.util.Scanner;

//數組模擬循環隊列
public class CircleArrayQueueDemo {

	public static void main(String[] args) {
		CircleQueue cq = new CircleQueue(4); //有效數據3個
		char key = ' '; //接收用戶輸入
		Scanner scanner = new Scanner(System.in);
		boolean loop = true;
		//輸出一個菜單欄
		while(loop){
			System.out.println("s(show): 打印隊列");
			System.out.println("a(add): 入隊列");
			System.out.println("g(get): 出隊列");
			System.out.println("h(head): 打印隊頭");
			System.out.println("e(exit): 退出程序");
			key = scanner.next().charAt(0);
			switch (key) {
			case 's':
				cq.showQueue();
				break;
			case 'a': //入隊列
				try {
					System.out.println("請輸入一個整數");
					int value = scanner.nextInt();
					cq.addQueue(value);  //最好判斷一下value是否是整數
				} catch (Exception e) {
					// TODO: handle exception
					System.out.println(e.getMessage());
				}
				break;
			case 'g': //出隊列
				try {
					int res = cq.getQueue();
					System.out.printf("出隊列的整數爲%d\n", res);
				} catch (Exception e) {
					// TODO: handle exception
					System.out.println(e.getMessage()); //輸出getQueue()方法中定義好的異常信息
				}
				break;
			case 'h': //打印隊頭
				try {
					int res = cq.headQueue();
					System.out.printf("隊列頭的整數爲%d\n", res);
				} catch (Exception e) {
					// TODO: handle exception
					System.out.println(e.getMessage());
				}
				break;
			case 'e': //退出
				scanner.close();
				loop = false;
				break;
			default:
				break;
			}
		}
		System.out.println("程序退出");
	}
}


 //使用數組模擬循環隊列,CircleQueue類
class CircleQueue{
	private int maxSize; //數組的最大容量
	private int front;   //隊列頭
	private int rear;   //隊列尾,指向隊列尾部後一個元素的位置
	private int[] arr;  //該數組用於存放數據,模擬隊列
	
	//隊列的構造方法
	public CircleQueue(int arrMaxSize){
		maxSize = arrMaxSize;
		arr = new int[maxSize];
		front = 0;  //隊列頭部
		rear = 0;  //隊列尾部,指向隊列尾部後一個元素的位置,front=rear時隊列爲空
	}
	
	//判斷隊列是否爲滿隊列,由於預留了一個空間,因此要rear+1
	public boolean isFull(){
		return front == (rear + 1) % maxSize;
	}
	
	//判斷隊列是否爲空隊列,跟簡單隊列的條件同樣
	public boolean isEmpty(){
		return rear == front;
	}
	
	//入隊列
	public void addQueue(int n){
		if(isFull()){
			throw new RuntimeException("隊列滿,不能添加數據了");
		}
		arr[rear] = n;
		rear = (rear + 1) % maxSize; //rear要循環
	}
	
	//出隊列
	public int getQueue(){
		if(isEmpty()){
			//拋出異常
			throw new RuntimeException("隊列空,不能出隊列了");
		}
		int value = arr[front];
		front = (front + 1) % maxSize; //front要循環
		return value;

	}
	
	//打印隊列
	public void showQueue(){
		if(isEmpty()){
			System.out.println("隊列空");
			return;
		}
		// 打印隊列中的有效元素,注意這裏i的邊界條件,size()方法見下面
		for (int i = front; i < front + size(); i++){
			int index = i % maxSize; //index爲循環後的下標
			System.out.printf("arr[%d]=%d\n",index, arr[index]); //格式化輸出
		}
	}
		
	
	//打印隊頭,不是出隊列
	public int headQueue(){
		if (isEmpty()){
			//拋出異常
			throw new RuntimeException("隊列空,不能打印頭");
		}
		return arr[front];  
	}
	
	//求出當前隊列的有效數據個數
	public int size(){
		return (rear - front + maxSize) % maxSize;
	}
}

reference

韓順平數據結構

大話數據結構

相關文章
相關標籤/搜索