您的位置:首页 > 理论基础 > 数据结构算法

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

2014-05-19 22:25 288 查看

一、队列

队列是线性表的一种特殊形式,遵循“先进先出”的原则。队列中一般包含两个指针:一个指针(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;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: