Java基础 - 队列的顺序存储结构及实现

队列(Queue)另外一种被限制的线性表,它使用固定的一端来插入数据,另一端只用于删除元素。也就是说,队列中的元素的移动方向总是固定的,就像排队购物一样:先进入队伍的顾客先获得服务,队伍中的顾客总是按固定方向移动,只有当排在自己前面的所有顾客获得服务后,当前顾客才能获得服务。

队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,只允许在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除的端称为队头。如果队列中不包含任何元素,该队列就被称为空队列。

对于一个队列来说,每个元素总是从队列的rear端进入队列,然后等待该元素之前的所有元素出队列后,当前元素才出对。因此,把队列简称为先进先出(FIFO)的线性表。对列示意图如下:


对列的常用操作:
对列同样作为线性表,通常不该提供线性表的如下方法:
(1)获取指定索引处的元素。
(2)按值查找数据元素的索引位置。
(3)在指定索引处插入新元素。
(4)删除指定索引处的元素。
从上面的这些方法可以看出,队列不应该提供从中间任意位置访问元素的方法。也就是说,队列只允许在队列的前端(front)删除元素和后端(rear)添加元素。

队列通常有如下一些方法:
(1)初始化:通常是一个构造器,用于创建一个空队列。
(2)返回队列的长度:该方法用于返回队列中数据元素的个数。
(3) 加入元素:向队列的后端(rear)添加新元素,队列的长度 +1。
(4) 删除元素:从队列的前端(front)删除元素,队列的长度 -1,并放回该元素。
(5) 访问队列前端元素:返回队列的前端(front)元素,但不删除该元素。
(6)判断队列是否为空:该方法判断队列是否为空,如果为空则返回true;否则返回false。
(7)清空队列元素:该方法清空队列的所有元素。
对于队列这种数据结构,上面的加粗字体就是他的标志性方法。类似于线性表的实现可以用顺序存储结构也可以用链式存储结构。队列也可以用顺序存储结构和链式存储结构这两种接过来实现。


系统采用一组连续的存储单元来依次保存队列的rear端到front端的所有数据元素,程序只需front和rear两个整形变量来记录队列的front端的元素索引和rear端的元素索引。顺序存储结构的队列简称为顺序队列。如下图所示顺序队列示意图:


从图中可以看出,顺序队列的front总是保存着队列中即将出列的数据元素的索引;顺序队列中的rear总是保存着下一个即将进入队列的元素的索引。队列中的元素的个数为rear-front。

下面是代码实现:

import java.util.Arrays;

public class SequenceQueue <T> {
	//默认队列容量为10
	private final int DEFAULT_CAPACITY = 10;
	//底层数组
	private Object[] elementData;
	//容量
	private int capacity;
	//
	private int front = 0;
	private int  rear = 0;
	
	public SequenceQueue(){
		this.capacity = DEFAULT_CAPACITY;
		elementData = new  Object[capacity];
	}
	public SequenceQueue(T element){
		this();
		elementData[0] = element;
		rear ++;
	}
	public SequenceQueue(T element, int initCapacity){
		this.capacity = initCapacity;
		elementData = new  Object[capacity];
		elementData[0] = element;
		rear ++;
	}
	
	//队列长度
	public int length(){
		return rear - front;
	}
	//判是否为空
	public boolean isEmpty(){
		return rear == front;
	}
	//向队列rear端插入元素
	public void add(T element){
		indexOutOfBoundsForAdd(rear);
		elementData[rear ++] = element;
	}
	//向队列front端删除并返回 该元素
	@SuppressWarnings("unchecked")
	public T remove(){
		noneElementForRemove();
		
		T t = (T) elementData[front];
		elementData[front ++] = null;
		return t;
	}
	//element()返回但不删除front端元素
	@SuppressWarnings("unchecked")
	public T element(){
		noneElementForRemove();
		
		return (T) elementData[front];
	}
	//清空队列
	public void clear(){
		Arrays.fill(elementData, null);
		front = 0;
		rear = 0;
	}
	//toString 方法
	public String toString(){
		if(isEmpty()){
			return "[]";
		}
		else{
			StringBuilder sb = new StringBuilder("[");
			for(int i = front; i < rear; i ++){
				sb.append(elementData[i].toString() + ",");
			}
			return sb.toString().substring(0, sb.length() - 1) + "]";
		}
	}
	
	
	private void indexOutOfBoundsForAdd(int index){
		if(index > capacity - 1){
			throw new IndexOutOfBoundsException("exception for SequenceQueue is filled:index of add is " + index);
		}
	}
	private void noneElementForRemove(){
		if(isEmpty()){
			throw new IndexOutOfBoundsException("none any element in SequenceQueue!");
		}
	}
}
测试代码如下:

import com.yc.list.SequenceQueue;

public class SequenceQueueTest {
	public static void main(String[] args) {
		 SequenceQueue<String> queue = new SequenceQueue<String>("aaa", 4);
		 queue.add("bbb");
		 queue.add("ccc");
		 queue.add("ddd");
		 System.out.println( "队列为:    " + queue);
		 System.out.println();
		 
		 String sElement = queue.element();
		 System.out.println( "队首为:    " + sElement);
		 System.out.println( "队列为:    " + queue);
		 System.out.println();
		 
		 String sRemove = queue.remove();
		 System.out.println( "队首为:    " + sRemove);
		 System.out.println( "队列为:    " + queue);
		 System.out.println();
		 
		 queue.add("eee");
	}
}

运行结果为:

从上面的运行结果来看,当程序执行 String sRemove = queue.remove();这句后,队首的值  aaa 从队列移除,此时队列有3个元素,讲道理程序再执行add.("eee");是可行的,

但出现了上面的异常,这也是这个实现的代码的不足之处:出现“假满”现象。


对于顺序队列而言,队列底层将采用数组来保存队列元素,每个元素在队列中的位置是固定不变的,变的只是rear和front两个整形变量。但有元素进入队列时,rear变量的值 +1;当有元素出列是,front的值 +1。(他妈的,这样做好像并没有释放底层数组里的元素引用啊!?)

对于上面的顺序队列的程序实现,数据元素在底层数组中的位置是固定的,改变的只是rear、front两个整形变量的值。这个队列可能出现所谓的“假满”现象。如下图所示:

从图中可以看出,此时rear等于该队列底层的数组容量capacity,如果此时试图向队列中添加元素,将会引起“队列已满”异常。其实这是一种“假满”现象。
此时该队列底层的数组依然有6个空位可以存储数据元素,但程序已经加不进去了。
对于假满问题,程序有如下解决方法:
(1)每次讲元素移除对了时都将队列中的所有元素向front端移动一位,这种方式下front值永远为 0 ,有元素每次插入队列时rear值+1,有元素移除时队列rear值-1.但这种方式非常浪费时间,
因为每次将元素从队列中删除时都需要进行“整体搬家”。
(2)将数组存储区看成是一个收尾相连的环形区域,当存放到数组的最大地址之后,rear的值再次变为 0 。采用这种技巧存储的队列称为循环队列。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值