【数据结构】循环队列的简单实现

什么是循环队列?

数组队列:https://blog.csdn.net/qq_42370146/article/details/82867649

前面我写到数组队列的一个很大的弊端,就是出队时删除头元素,相当于使数组后面的元素全部往前挪一位,即数组进行覆盖操作,其时间复杂度为O(n),而循环队列可以解决此弊端,使全部操作复杂度都变为O(1)。

假设一个数组队列已满,出队时删除头元素,我们不需要让数组元素往前移一位,只需要在入队时使元素插入到数组前端的空白处,此时虽然入队元素在数组前端,但仍作为队首,这样就可以优化出队时的时间复杂度,循环队列只是在数组队列的基础上进行优化。

--------------------------------------------------------------------------------------------------------------------------------------------------------

理解循环队列

front------>指向队首元素

tail-------->指向队尾元素的下一个索引位置

capacity----->数组大小

初始化队列为空,使front和tail都指向数组索引为0的位置,即当front=tail是队列为空。

入队操作:tail=(tail+1)%capacity

出队操作:front=(front+1)%capacity

队列为满:(tail+1)%capacity=front

扫描二维码关注公众号,回复: 3495591 查看本文章

至于为什么对数组大小求余是实现循环的关键,模拟一下即可以清楚其原理,循环的实现让我们有意识的在数组在浪费一个空间。

----------------------------------------------------------------------------------------------------------------------------------------------------------------

循环队列的实现

循环队列我们不再复用我们之前二次封装的动态数组类,而是重新从底层写起。

泛型类:LoopQueue

实现接口:Queue

成员变量(私有化):

E[ ] data,int front,int tail,int size

成员方法:

①LoopQueue()             构造方法,实例化对象时即指定数组大小

②int getCapacity()        得到队列大小

③boolean isEmpty()     判断队列是否为空

④int getSize()                得到队列元素个数

⑤void enqueue()           入队

⑥E dequeue()               出队

⑦void resize(int newCapacity)   动态扩容或缩容

⑧E getFront()              查看队首元素

⑨String toString()   重写Object类的方法

----------------------------------------------------------------------------------------------------------------------------------------------------------

public class LoopQueue<E> implements Queue<E> {
	private E[] data;
	private int front, tail, size;

	// 构造方法,自定义队列长度
	public LoopQueue(int capacity) {
		data = (E[]) new Object[capacity + 1];
		front = 0;
		tail = 0;
		size = 0;
	}

	// 构造方法,得到长度为10的队列
	public LoopQueue() {
		this(11);
	}

	// 得到队列大小
	public int getCapacity() {
		return data.length - 1;
	}

	@Override
	// 判断队列是否为空
	public boolean isEmpty() {
		return front == tail;
	}

	@Override
	// 得到队列中元素个数
	public int getSize() {
		return size;
	}

	@Override
	// 入队
	public void enqueue(E e) {
		if ((tail + 1) % data.length == front)
			resize(getCapacity() * 2);
		data[tail] = e;
		tail = (tail + 1) % data.length;
		size++;
	}

	// 改变队列长度
	private void resize(int newCapacity) {
		E[] newData = (E[]) new Object[newCapacity + 1];
		for (int i = 0; i < size; i++)
			newData[i] = data[(i + front) % data.length];
		data = newData;
		front = 0;
		tail = size;
	}

	@Override
	// 查看队首元素
	public E getFront() {
		if (isEmpty())
			throw new IllegalArgumentException("队列为空,操作失败!");
		return data[front];
	}

	@Override
	// 出队
	public E dequeue() {
		if (isEmpty())
			throw new IllegalArgumentException("队列为空,操作失败!");
		E temp = data[front];
		data[front] = null;
		front = (front + 1) % data.length;
		size--;
		if (size == getCapacity() / 4 && getCapacity() / 2 != 0)
			resize(getCapacity() / 2);
		return temp;
	}

	@Override
	// 重写toString()方法
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(String.format("Queue:size=%d,capacity=%d\n", size, getCapacity() - 1));
		sb.append(" front [");
		for (int i = front; i != tail; i = (i + 1) % data.length) {
			sb.append(data[i]);
			if ((i + 1) % data.length != tail)
				sb.append(", ");
		}
		sb.append("] tail");
		return sb.toString();
	}
}

虽然循环队列比数组队列复杂些,但是这种算法优化无疑是值得的,在出队的执行效率上相差了上百倍,不得不感叹算法之妙,队列应用很广,现在实现的只是队列中很基础的形式,在以后深入会学习更多队列内容,例如经常在比赛里用到的广度优先算法,就是二叉树+队列的应用,所以学习数据结构会对算法的本质有更深层次的理解,ojbk!!

猜你喜欢

转载自blog.csdn.net/qq_42370146/article/details/82901072