【数据结构】——队列

一、队列的定义及特点

队列是一种只允许在表的一端删除(称作表头)和表的另一端加入(称作表尾)的线性表。

从定义可以看出队列的特点是先进先出。

二、顺序表示的循环队列

概括来说循环队列是通过取模操作解决队列空间假溢出的问题,能更充分的利用所分配空间。因为在一般队列的顺序表示中,无论增加还是删除,头指针或尾指针都会递加,这样就会导致数组的低索引位置不可再被使用。

这种弊端在链式表示中不存在,因为链式表示中并不是一次开辟所有空间,而是动态开辟,并且删除的结点空间可以被释放。

这里只实现顺序表示的循环队列,个人认为链表的循环没有太大意义,如果只是循环而不增加队列长度,那么顺序表示更方便,如果需要增加队列长度的话,附带头尾指针的链表同样可以替代。

详细过程见代码

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在断开结点所有引用之后会自动回收,所以省略了这一步。

补充:用两个栈实现队列

很简单,具体代码就不写了,思路是一个栈用作入队,一个栈用作出队,当出队栈空时将入队栈中的所有元素依次弹出压入到出队栈中。

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