1.队列
说到队列首先给我们的第一印象就是“先进先出”,这一点与栈是完全相反的。当然,对于双端对列除外,因为其可以指定任意一端进行数据的删除和插入。其次在队列中的删除和插入是采用环绕式处理的方式进行的,即通过头标记head_sign和尾标记rear_sign的移动来实现的,具体过程可以通过下边的图进行理解。
2.队列结构实现
public class Queue{
//record the head of queue(记录对头)
private int head_sign=0;
//record the tail of queue(记录队尾)
private int rear_sign=-1;
//record the num of element(记录队列中元素个数)
private int size;
//记录队列申请到的长度
private int maxLength;
//queue(存放队列元素的内部数组)
private char[] queueArray=null;
//stop overwrite Queue()(将无参构造器设置为私有以避免用户通过其创建出无意义的对象)
private Queue(){}
//create queue by constructor(有参构造器用来创建一个空的队列)
public Queue(int size){
queueArray=new char[size];
maxLength=size;
//get the begin size of queueArray
this.size=0;
}
//insert element(向队列添加元素)
public void insert(char element){
if(!isFull()){
//每添加一个元素,指向队尾的标记要向上移动一次
rear_sign++;
//由于要进行环绕式处理,所以在队列中有元素且队尾标记已经指向最高的情况下,将其指向数组初始位置
if(rear_sign==maxLength&&head_sign>0){
rear_sign=0;
queueArray[rear_sign]=element;
}else{
queueArray[rear_sign]=element;
}
size++;
}else{
System.err.println("Sorry,this queue is full!");
}
}
//remove element(元素删除)
public void remove(){
if(size>0){
//每删除一个元素,队头标记要向上移动一位以保证队头元素可以方便出队
head_sign++;
//环绕式处理需要保证在队列中有数据且队头标记已经指向队列最高位置时环绕到数组初始端
if(head_sign==maxLength){
head_sign=0;
}
if(--size==0){
rear_sign=-1;
head_sign=0;
}
}else{
System.err.println("Sorry,this queue no element!");
}
}
//peek element(弹出队头数据却不删除)
public char peek(){
char element=queueArray[head_sign];
return element;
}
//isEmpty(判断队列是否为空)
public boolean isEmpty(){
if(size<=0){
return true;
}else{
return false;
}
}
//isFull(判断队列是否已满)
public boolean isFull(){
if(size==maxLength){
return true;
}else{
return false;
}
}
//get the size of queueArray(获取当前对列的元素个数)
public int size(){
return size;
}
}
3.测试策略
键盘输入一字符串,通过charAt()方法取出字符串中的每一个字符并插入到一个新的空队列中,在一系列的测试中我们只需要验证如下方法是否可以按照队列的功能要求正常实现:
- 是否可以正常插入数据到队列。
- 是否可以遍历的每次将队列的头数据删除,即进行先进先出。
- 是否可以正常变量弹出头数据却不删除数据本身。
- 是否在插入数据时保证尾标记上升,头标记不变。
- 是否在删除数据时保证头标记上升而尾标记不变。
- 是否可以实现队列的环绕式处理,具体在代码中的注释详细表明。
具体测试代码:
import java.util.Scanner;
public class QueueTest{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
System.out.println("Please input a element:");
//通过键盘输入获取要入队的数据
String info=sc.nextLine();
//create a queue(获取一个空的队列)
Queue queue=new Queue(info.length());
for(int in=0;in<info.length();in++){
queue.insert(info.charAt(in));//通关charAt()方法将字符串中的每一字符插入队列中(输入为123456)
}
System.out.println("\nBegin_size():"+queue.size());//显示第一次入队后队列的元素个数(为6)
//show all element(通过显示队头然后弹出更新到新的队头的方式显示队列中现有的所有元素,以检验队列是否可以用)
for(int in=0;in<info.length();in++){
if(!queue.isEmpty()){
System.out.print(queue.peek()+" ");//peek()显示当前队头元素却不删除元素
}
queue.remove();//删除当前队头元素(以更新为新的队头)
}
System.out.println("\nAfterRemove_size():"+queue.size());//遍历完队列的同时也删除了所有的原素(此时元素个数为0)
//insert all(再次插入上述元素123456)
for(int in=0;in<info.length();in++){
queue.insert(info.charAt(in));
}
System.out.println("\nInsert_size():"+queue.size());//此时队列中的元素个数为6
//测试环绕式处理是否可用,先连续删除四个元素,此时队头和队尾标记(其在删除操作不做移动)应该标记在数组的小标位置为4和5
queue.remove();
queue.remove();
queue.remove();
queue.remove();
//紧接着连续插入5个元素(a,b,c,d,e),此时队头标记不做移动,但是之前队尾标记已经到达队列最高端,所以需要进行环绕操作,否则会溢出
queue.insert('a');
queue.insert('b');
queue.insert('c');
queue.insert('d');
queue.insert('e');
System.out.println("\nsize():"+queue.size());//由于在插入第五个元素队列已经满了,所以只插入4个,最终元素个数仍为6
System.out.println("isEmpty():"+queue.isEmpty());//此时队列不为空(false)
System.out.println("isFull():"+queue.isFull());//此时为满队列(true)
System.out.println("---------最终元素显示(56abcd)-------------");
while(!queue.isEmpty()){
System.out.print(queue.peek()+" ");
queue.remove();
}
System.out.println("\nsize():"+queue.size());//此时已经删除了所有元素,所以个数为0
System.out.println("isEmpty():"+queue.isEmpty());//此时队列为空(true)
System.out.println("isFull():"+queue.isFull());//此时队列不满(false)
}
}
运行效果:
从运行结果可以看出此队列的几个特点:
- 先进先出。
- 环绕式处理使得队列可以循环进行入队和出队操作,但使用者是感觉不到的。
--------------------------------
在实际的队列使用中还有一种优先级队列,其通常使用堆的数据结构进行实现,但是数组也可以实现,其于上述的一般队列基本相似,但是唯一区别在于在插入数据时要对数据按照一定标准进行排序,一般会将优先级最高的数据放在队列头部,因为在出队时效率快,时间复杂度为O(1),但是由于插入时需要排序,所以时间复杂度为O(N)。