数据结构之队列的顺序存储

本文介绍了队列这一特殊线性表,遵循“先进先出”原则,详细阐述了顺序存储队列的概念、操作及可能出现的“虚满”问题。接着,文章重点讲解了循环队列,作为解决“虚满”问题的有效手段,提高了空间使用效率。通过循环队列的结构特点,分析了其入队、出队操作,并提供了相关的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、队列

队列是线性表的一种特殊形式,遵循“先进先出”的原则。队列中一般包含两个指针:一个指针(front)用来指向队首,另一个指针(rear)用来指向队尾。队列的操作如下图的a~i所示。


上图中我们假设队列的最大长度为4。图a表示队列为空队,此时队首和队尾都是指向-1的;当元素A进队后(图b),队首不变,队尾指向元素A(下标为0);当元素B进队后(图c),队首不变,队尾指向元素B(下标为1);当元素C进队后(图d),队首不变,队尾指向元素C(下标为2);当元素D进队后(图e),队首不变,队尾指向元素D(下标为3),此时队列为满队,不允许再有元素入队了;当元素A出队时(图f),队首右移一个元素位置,队尾不变;以此类推,当元素D出队时(图i),队首右移一个元素位置,此时队首和队尾相遇了,表示队列为空。

总结:

1.队列是一种特殊的线性表,遵循“先进先出,后进后出”的原则,就跟我们在火车站排队买票一样;

2.队列为空时,front与rear指向同一位置;

3.队列为满队时,rear指向数组的最后一个元素;

4.采用普通的顺序存储队列时,队列存在“虚满”的情况,即rear指向数组的最后一个元素时,队列不一定是真正意义上的满队,有可能若干队首元素已经出队,如图f~h所示。为了防止“虚满”情况发生,可以采用以下两种方式:(1)队首元素出队后,将数组中的所有元素向前移动一格;(2)采用循环队列的方式。方式一的时间复杂度为O(n),方式二的时间复杂度为O(1),因此更多采用循环队列的方式来降低队列算法复杂度。

普通队列的实现代码如下所示。在队列类中,封装了队列构造、判断队列是否为空、判断队列是否为满队、入队、出队、获取队首元素等操作的方法。

public class Queue {
	private Object[] a;
	private int length = 100;
	private int front = -1;
	private int rear = -1;
	
	/**
	 * 构造函数
	 */
	public Queue() {
		a = new Object[length];
	}
	/**
	 * 重载构造函数
	 * 
	 * @param length 初始化队列的长度
	 */
	public Queue(int length) {
		this.length = length;
		a = new Object[length];
	}
	
	/**
	 * 判断队列是否为空
	 * 
	 * @return 如果队列为空,返回true;否则,返回false
	 */
	public boolean isEmpty() {
		return front == rear;
	}
	
	/**
	 * 判断队列是否为满队。该函数并不能真正判断队列是否已满,因为有可能一些元素已出队,
	 * 因此在队首存在空缺。如果需要判断真正的队满,请参见循环队列中的判断队满函数。
	 * 
	 * @return 如果队列为满队,返回true;否则,返回false
	 */
	public boolean isFull() {
		return rear == length - 1;
	}
	
	/**
	 * 获取队列中的元素,需要判断队列是否为空队
	 * 
	 * @return 如果队列为空,返回null;否则,返回队首元素
	 */
	public Object get() {
		return isEmpty() ? null : a[front + 1];
	}
	
	/**
	 * 将队列中的队首元素出队,需要判断队列是否为空队
	 * 
	 * @return 如果队列为空,返回null;否则,返回队首元素
	 */
	public Object outQueue() {
		return isEmpty() ? null : a[++front];
	}
	
	/**
	 * 向队尾添加元素item,需要判断队列是否为满队
	 * 
	 * @param item 向队尾中插入的元素
	 * @return 如果队满,返回false;否则,返回true
	 */
	public boolean inQueue(Object item) {
		if(isFull()) {
			return false;
		} else {
			a[++rear] = item;
			return true;
		}
	}
}

二、循环队列

循环队列是队列的顺序存储结构的一种优化,主要是为了解决队列“虚满”的问题,提升队列空间的使用效率。循环队列采用存储结构同样是数组,只是将数组看成是一个环形结构而已。如下图所示。


数组A[0...N]是一个具有N+1个元素的数组,我们假设该数组的头(0位置)和尾(N位置)是相接的。当队列中不存在任何元素时,队首(front)和队尾(rear)均指向数组下标为0的结点;当有元素A入队时,先将队尾(rear)右移一个位置至下标1,并将下标1位置写入元素A;当有元素出队时,先将队首(front)右移一个位置至下标1,并获取下标1位置的元素;当位置N处已经存在元素时,此时如果还有元素要入队,则判断数组下标为0的位置是否为队首,如果是,则表明队满了,如果不是,则将需要入队的元素写入下标0的位置。以此类推。

不难看出:循环队列的N+1个位置中,队首所指位置始终是不存在元素的,但不可否认,这一个位置的牺牲还是很有必要滴。

循环队列的实现代码如下。同样,代码中封装了判断队空、判断队满、入队、出队、获取队首元素等方法。

public class CycleQueue {
	private Object[] a;
	private int length = 100;
	private int front = 0;
	private int rear = 0;
	
	/**
	 * 构造函数
	 */
	public CycleQueue() {
		a = new Object[length];
	}
	/**
	 * 重载构造函数
	 * 
	 * @param length 初始化队列的长度
	 */
	public CycleQueue(int length) {
		this.length = length;
		a = new Object[length];
	}
	
	/**
	 * 判断队列是否为空
	 * 
	 * @return 如果队列为空,返回true;否则,返回false
	 */
	public boolean isEmpty() {
		return front == rear;
	}
	
	/**
	 * 判断队列是否为满队
	 * 
	 * @return 如果队列为满队,返回true;否则,返回false
	 */
	public boolean isFull() {
		return (rear + 1) % length == front;
	}
	
	/**
	 * 获取队列中的元素,需要判断队列是否为空队
	 * 
	 * @return 如果队列为空,返回null;否则,返回队首元素
	 */
	public Object get() {
		return isEmpty() ? null : a[(front + 1) % length];
	}
	
	/**
	 * 将队列中的队首元素出队,需要判断队列是否为空队
	 * 
	 * @return 如果队列为空,返回null;否则,返回队首元素
	 */
	public Object outQueue() {
		if(isEmpty()) {
			return null;
		} else {
			front = (front + 1) % length;
			return a[front];
		}
	}
	
	/**
	 * 向队尾添加元素item,需要判断队列是否为满队
	 * 
	 * @param item 向队尾中插入的元素
	 * @return 如果队满,返回false;否则,返回true
	 */
	public boolean inQueue(Object item) {
		if(isFull()) {
			return false;
		} else {
			rear = (rear + 1) % length;
			a[rear] = item;
			return true;
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值