设计循环队列

循环队列是表示内部是循环的,但操作依然是只能往队尾添加元素,但各项操作优化到O(1)。

这是接口设计

public class CircleQueue {
    // 记录第0个元素的索引
    private int front;
    // 当前队列存储的元素个数
    private int size;
    // 用来存储元素的数组
    private E[] elements;
    // 当前队列存储的元素数量
    public int size();
    // 当前队列是否为空
    public boolean isEmpty();
    // 入队
    public void enQueue(E element);
    // 出队
    public E deQueue();
    // 查看索引为0的元素
    public E front();
}

这里只介绍几个重要的方法。
入队:
首先需要判断数组是否需要扩容

private void ensureCapacity(int capacity) {
    int oldCapacity = elements.length;
    if (oldCapacity >= capacity) return;
        
    // 新容量为旧容量的1.5倍
    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;
        
    // 重置front
    front = 0;
}

然后计算在数组中入队的索引

  • 预期入队索引 = 第0个元素索引 + 当前队列元素个数
  • 如果预期入队索引大于等于数组长度实际入队索引 = 预期入队索引 - 数组长度
  • 如果预期入队索引小于数组长度实际入队索引 = 预期入队索引
private int index(int index) {
    index += front;
    return index - (index >= elements.length ? elements.length : 0);
}

所以入队的方法为

public void enQueue(E element) {
    // 数组扩容判断,要加新元素,需要的容量就是当前容量加一
    ensureCapacity(size + 1);
    // 索引计算,并赋值
    elements[index(size)] = element;
    // size加一
    size++;
}

出队,就移动front指针

public E deQueue() {
    // 获取出队元素
    E frontElement = elements[front];
    // 将索引位置致空
    elements[front] = null;
    // 更新font,这里参数为1
    front = index(1);
    // size减一
    size--;
    // 返回出队元素
    return frontElement;
}

至此,循环队列的方法就介绍完了。值得探讨的是index这个方法,很精妙。
这篇文章参考了小码哥的笔记,附上链接
参考博文

你可能感兴趣的:(java)