基于Java实现的队列

队列

1. 队列的概念

  • 队列和链表来说非常的相似的,队列同样是前一个节点连接后一个节点,同样是线性结构;但是队列不同与链表的就是队列只允许尾部插入(入队),头部删除(出队),而链表是允许所有位置的操作,所以队列的情况与操作相对于链表要简单所以在这里我们就不再详细的解释普通队列的实现(需要实现的话可以在我上次写的链表操作中只留下尾部插入的代码即可)

循环队列

  • 概念:对于队列来说循环队列才是需要注意 情况,他提高内存的利用效率同时也更加难以实现。循环队列相当于把队列的头和尾连接起来,用一个指针标记头结点的位置,这样头结点就不是固定可以在循环队列上面到处移动,只需要记住标记的位置就可以和队列中元素的个数就可以实现队列循环操作。

单循环队列

1. 基本实现

  • 对于单循环队列实现我们依靠数组来是实现,使用front标记头结点的索引位置,然后通过计算得到尾结点的索引位置(具体到相应方法时会说明相应的计算方法),对于数组来说front头节点,所以只要出队我们就把front记录的索引后移一位,然后长度减一。如果刚好在数组的最后一个索引的位置是头结点front,那么我们进行出对操作之后,头节点front就应该移动到数组的起始索引位置从新开始计算,添加元素也是一样的操作,如果循环队列满了的话,我们就需要扩容(操作和动态数组是一样的)

2. 代码实现

  • 获取真实索引:由于我们是在使用动态数组模拟一个循环队列操作,所以我们需要得到一个元素在数组的真实索引,并且把对应到循环数组的位置上去。下面我们用一个栗子来讲解,当前队列存在两个元素,而且存在当前数组的末尾,现在我们还想再添加一个元素,那么应该添加到那儿?我们认为这个动态数组其实是一个循环队列,所以我们也就可以想象他是首尾相连的,那么这样我们添加的下一个元素就是添加在数组的开头位置;这是我们直接看出来的,对于代码的操作来说他只是在队列的末尾进行添加操作,而不会去关系到底应该加到那一个位置,所以我们需要找到当前添加节点与数组索引的对应关系,而这个关系可以使用一个公式来得到头结点索引 + 当前队列长度 / 整个数组的长度得到的就是我们新插入元素的真实索引位置。
    添加
/**
	 * 由于(front + size) % elements.length都是在获得真实索引,循环队列索引的映射
	 * @param index
	 * @return
	 */
	private int index(int index) {//传入元素位于当前队列的位置(出队都是1)
		int x = index+front;
		//判断是否横跨头尾,是的话就减去数组长度
		return x - ((x > elements.length) ? elements.length : 0);
	}
  • 添加元素:对于添加元素来说,就是在队列的末尾添加元素,对应到数组索引是0开始就是往队列size的位置添加元素,所以我们就把size传入index()函数中,去计算真实的索引位置。(出队操作,就是把传入的值设定为1就行了,因为队列都是从头出对,所以都是第一个元素出队)
/**
	 * 循环队列的入队操作
	 * @param elements
	 */
	public void enQueue(E e) {
		/*
		 * 循环队列的入队操作,不能使用front+size来添加元素,否则会出现数组越界的异常
		 * 解决:我们需要对数组的空间长度进行取模操作,然后得到的值就是我们可以添加的位置
		 */
		ensureCapacity(size + 1);
		elements[index(size)] = e;	//这里使用index()函数转换索引操作
		size++;
		
	}
  • 完整代码
public class CircleQueue<E> {
	
	private int front;
	private int size;
	private E[] elements;
	private static final int DEFAULT_CAPACITY = 10;
	
	public CircleQueue() {
		elements = (E[]) new Object[DEFAULT_CAPACITY];
	}
	
	/**
	 *获取当前数组的元素个数
	 * @return
	 */
	public int size() {
		return size;
	}
	
	/**
	 * 判断当前队列是否为空
	 * @return
	 */
	public boolean isEmpty() {
		return size == 0;
	}
	
	/**
	 * 循环队列的入队操作
	 * @param elements
	 */
	public void enQueue(E e) {
		/*
		 * 循环队列的入队操作,不能使用front+size来添加元素,否则会出现数组越界的异常
		 */
		ensureCapacity(size + 1);
		elements[index(size)] = e;	//这里使用index()函数转换索引操作
		size++;
		
	}
	
	/**
	 * 循环队列的出队操作
	 * @return
	 */
	public E deQueue() {
		/*
		 *对头元素,获取之后清空该位置的元素,然后把front的位置右移一位,循环移动,所以也需要模以长度 
		 */
		E e = elements[front];
		elements[front] = null;
		front = index(1);	//这里使用函数转换索引操作
		size--;
		return e;
	}
	
	/**
	 * 获取当前的队头元素值
	 * @return
	 */
	public E front() {
		return elements[front];
	}
	
	/**
	 * 数组的动态扩容
	 * @param capacity
	 */
	public void ensureCapacity(int capacity) {
		// 先获取旧容量,判断需要扩容则扩容,反之什么都不做
		int oldCapacity = elements.length;
		if (oldCapacity >= capacity) return;
		// 扩展的容量这里不做修改,仍然是左移一位,扩大两倍
		int newCapacity = oldCapacity + (oldCapacity >> 1);
		E[] newElements = (E[]) new Object[newCapacity];
		for (int i=0; i<size; i++) {
			newElements[i] = elements[index(i)];	//这里我们使用了索引映射方法
		}
		elements = newElements;
		// 重置数组下标为0
		front = 0;
	}
	
	/**
	 * 由于(front + size) % elements.length都是在获得真实索引,循环队列索引的映射
	 * @param index
	 * @return
	 */
	private int index(int index) {
		int x = index+front;
		return x - ((x > elements.length) ? elements.length : 0);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值