【數據結構】——隊列

一、隊列的定義及特點

隊列是一種只允許在表的一端刪除(稱作表頭)和表的另一端加入(稱作表尾)的線性表。

從定義可以看出隊列的特點是先進先出。

二、順序表示的循環隊列

概括來說循環隊列是通過取模操作解決隊列空間假溢出的問題,能更充分的利用所分配空間。因爲在一般隊列的順序表示中,無論增加還是刪除,頭指針或尾指針都會遞加,這樣就會導致數組的低索引位置不可再被使用。

這種弊端在鏈式表示中不存在,因爲鏈式表示中並不是一次開闢所有空間,而是動態開闢,並且刪除的結點空間可以被釋放。

這裏只實現順序表示的循環隊列,個人認爲鏈表的循環沒有太大意義,如果只是循環而不增加隊列長度,那麼順序表示更方便,如果需要增加隊列長度的話,附帶頭尾指針的鏈表同樣可以替代。

詳細過程見代碼

public class CircularQueue {
	private int[] arr;
	private int maxSize;
	private int head; 			// 隊頭出隊
	private int tail; 			// 隊尾入隊
	private int size; 			// 隊列中存在的元素個數

	public CircularQueue(int size) { 		// 構造函數初始化相關信息
		maxSize = size + 1; 		// 這裏+1是因爲下面選用的隊列判空條件會導致少用一個元素空間
		arr = new int[maxSize];
		head = tail = this.size = 0; 	// 初始頭尾指針都指0
	}

	private boolean isEmpty() { 	// 判斷隊空
		return head == tail;
	}

	private boolean isFull() { 		
		// 判斷隊滿,這種取模操作必定會導致減少一個可使用空間,並且注意這個不可用空間並不固定。
		return (tail + 1) % maxSize == head;
	}

	public void inQueue(int num) {
		if (isFull()) {				// 入隊前判斷隊滿
			System.out.println("隊滿!");
		} else {
			arr[tail] = num;
			tail = (tail + 1) % maxSize;
			size++;
		}
	}

	public int outQueue() {
		if (isEmpty()) {			// 出隊前判斷隊空
			System.out.println("隊空!");
			return -1;
		} else {
			int num = arr[head];
			head = (head + 1) % maxSize;
			size--;
			return num;
		}
	}

	public int getSize() {			
		// 直接tail-head可能會出現負數,這是因爲循環過程中兩指針不斷移動,會出現tail<head的情況。
		return (tail - head + maxSize) % maxSize;
	}

}

在上面代碼中並沒有用到成員變量size的信息,size也可以用來判斷隊滿隊空且更加容易,但這就不是學習數據結構了~

三、鏈式表示的一般隊列

public class LinkedQueue {
	private class Node { 		// 使用單鏈表存儲
		int data;
		Node next;

		public Node() {

		}

		public Node(int num) {
			data = num;
		}
	}

	Node head; 					// 頭結點,指向隊列中的第一個結點
	Node tail;
	int size;

	public LinkedQueue() {
		head = new Node(); 				// 初始化,新建頭結點,尾指針指向頭結點
		tail = head;
		size = 0;
	}

	public void inQueue(int num) { 		// 入隊不需要判斷隊滿,入隊元素連接在隊尾,尾指針後移
		Node node = new Node(num);
		tail.next = node;
		tail = node;
		size++;
	}

	public int outQueue() { 			// 出隊需要判斷隊空
		if (head == tail) {
			System.out.println("隊空!");
			return -1;
		}

		if (head.next == tail) { 		// 隊列中只有一個結點時斷開頭結點的引用
			Node node = tail;
			head.next = null;
			tail = head;
			size--;
			return node.data;
		} else { 						// 否則頭結點指針指向這個結點的下一個結點
			Node node = head.next;
			head.next = head.next.next;
			size--;
			return node.data;
		}
	}

	public int getSize() {
		return size;
	}
}

隊列判空條件同樣可以使用成員變量size。鏈隊的刪除還應該注意要釋放結點空間,但因爲java中的GC在斷開結點所有引用之後會自動回收,所以省略了這一步。

補充:用兩個棧實現隊列

很簡單,具體代碼就不寫了,思路是一個棧用作入隊,一個棧用作出隊,當出隊棧空時將入隊棧中的所有元素依次彈出壓入到出隊棧中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章