与栈的后进先出(LIFO,Last In Frist Out)不同,队列是先进先出(FIFO,Frist In First Out),在现实中就像排队买票一样,每个人都得从队尾排队,然后排在队前的人才能先拿到票。
队列也是基本的数据结构,可以用双端链表(或者双向链表)、数组存储,我们这里用数组存储来解释循环队列。
什么是循环队列捏?假如有一辆过山车有5个位子,规定,进入过山车的人只能从车后进,车前出。这样,当五个人进入过山车后,这个队列就满了。这时,坐在最前面的人走了,应该有了个空座,可以进去一个人,但是按照规定人只能从车后进,所以不能进人,而循环队列就是相当把最后一个位子移到最后,从而使人进去。换成数组的话,就是把数组头与尾相连,形成一个圆(循环)。而使用这个数组存储的队列就叫循环队列。
循环队列与一般队列不同,需要做边界判断,因为你不知道,这个数组是否已经循环过,尤其是在做判空和判满操作时,你得考虑两种情况,在《Java数据结构与算法》中,作者做过处理,但是还是不好理解,尤其是当你没插一个数据与插满数据,队头标识位又回到起点的情况,所以我在程序中添加了一个是否已经循环的标识位,相当于,对队头队尾是否已经颠倒。其代码如下:
package test.queue;
public class Queue
{
//存储数组、队头、队尾、是否已经循环
private int[] items;
private int head;
private int tail;
private boolean isTraned;
public Queue(int size)
{
items = new int[size];
head = 0;
tail = 0;
isTraned = false;
}
//反转,用于控制计算整个队列的长度时,是否要跨边界。
private void tran()
{
isTraned = !isTraned;
}
//计算长度
public int size()
{
if(isTraned)//已经循环,需要跨边界计算
return items.length + tail - head;
else
return tail - head;
}
//判空
public boolean isEmpty()
{
return size()<=0;
}
//判满
public boolean isFull()
{
return size()>=items.length;
}
//插入
public void insert(int in)
{
if(isFull())
throw new IndexOutOfBoundsException("队列已满");
else
{
items[tail++] = in;
if(tail == items.length)
{
tail = 0;
tran();
}
}
}
//移除
public int remove(){
int out = 0;
if(isEmpty())
throw new IndexOutOfBoundsException("队列为空");
else
{
out = items[head++];
if(head == items.length)
{
head = 0;
tran();
}
}
return out;
}
//查看
public int peek(){
if(isEmpty())
throw new IndexOutOfBoundsException("队列为空");
else
return items[head];
}
public static void main(String[] args) {
Queue q = new Queue(3);
q.insert(3);
q.insert(2);
System.out.println(q.remove());
System.out.println(q.remove());
q.insert(11);
q.insert(12);
System.out.println(q.remove());
System.out.println(q.remove());
q.insert(100);
q.insert(101);
q.insert(102);
System.out.println(q.remove());
System.out.println(q.remove());
System.out.println(q.remove());
}
}
运行一下:
3
2
11
12
100
101
102
大家可以试试,如果在q.insert(102);后再insert。或者System.out.println(q.remove());再remove都会出现IndexOutOfBoundsException错误。
我们再来看看优先级队列。
优先级队列可以这样理解,就好像一个奇怪的电影院,也需要排队买票,但是他对规定:必须按照从高到矮的顺序排队。也就是说,新来的人必须在队伍中找到高矮正合适的位置,而先拿到票的永远是队伍中最矮的。
相对于前面循环队列,优先级队列没有队尾,队头即元素数,也不用判断边界,所不同的是需要一个插入算法,其代码如下:
package test.queue;
public class PriorityQueue
{
//存储数组、已有元素个数
private int[] items;
private int itemNum;
public PriorityQueue(int size)
{
items = new int[size];
itemNum = 0;
}
//个数
public int size()
{
return itemNum;
}
//判满
public boolean isFull()
{
return itemNum >= items.length;
}
//判空
public boolean isEmpty()
{
return itemNum <= 0;
}
//移除
public int remove()
{
if(isEmpty())
throw new IndexOutOfBoundsException("队列为空");
else
return items[--itemNum];
}
//插入
public void insert(int in)
{
if(isFull())
throw new IndexOutOfBoundsException("队列已满");
else
{
int i = itemNum - 1;
while(i >= 0 && items[i] < in)//按照从小到大的顺序排列,这里有点像插入排序。
{
items[i+1]=items[i];
i--;
}
items[i+1] = in;
itemNum++;
}
}
//查看队前元素
public int peek()
{
return items[itemNum - 1];
}
public static void main(String[] args) {
PriorityQueue pq = new PriorityQueue(3);
pq.insert(8);
pq.insert(4);
pq.insert(6);
System.out.println(pq.remove());
System.out.println(pq.remove());
System.out.println(pq.remove());
}
}
运行一下:
4
6
8
大家可以看出的确是从小到大输出了。