循环队列与优先级队列的Java实现

与栈的后进先出(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

大家可以看出的确是从小到大输出了。

你可能感兴趣的:(循环队列与优先级队列的Java实现)