队列就是:只能头删尾增,就跟我们去网红小店排队一样
主要使用环形数组实现是因为环形数组具有以下的优点:
1.移除头部元素后序的元素都不需要向前移动,只需要头指针移动即可
2.任意一个节点都可以当做头部尾部(一般数组都是0索引当做头部)
3.因为通过取模运算,形成了环形,不会出现数组越界的情况
4.数组自身就具有良好的性能,比如:局部性原理和cpu缓存
头尾指针指向同一位置即为空
正常思维的话:我们判满的条件不也是同一位置吗,但是如果这样就和判空冲突了
所以我们就需要通过来浪费空间来换取时间的效率了(牺牲一个数组元素来判满)
当 ( 尾 + 1)% 数组长度 = 头 就表示满了 (之所以%A数组长度就是因为:尾可能在数组最后在 + 1不会和0相等的)
可以对上述优化:
优化从start和end存的就是索引转换为逐渐递增的整数,这句话很抽象就跟B树与B+树的区别差不多
就是尾指针你只要不添加元素,我就不管你是不是越界,然后我判空的条件就是 尾 - 头 = 数组长度
这样其实也没有什么好优化的主要是可以少几次求摸运算
添加后尾指针移动一次, 删除后头指针移动一次
public class ArraysQueue implements Iterable{
private T[] array;//数组
private int start;//头指针
private int end;//尾指针
public ArraysQueue(int initialCapacity){
array = (T[]) new Object[initialCapacity + 1];//因为判满要牺牲一个所以要多创建一个
start = 0;
end = 0;
}
}
//判断是否为空
public boolean isEmpty(){
//如果头尾指针指向同一个位置既为空
return start == end;
}
//判满
public boolean isFull(){
return (end + 1) % array.length == start;
}
//入队
public void offer(T element) {
if (isFull()) {//判满
System.out.println("队列已满,无法入队");
return;
}
array[end] = element;//在尾部添加 (注意不许合并end++) 会越界的
end = (end + 1) % array.length;//尾巴向后移动一下
}
//删除并获取被删除的元素
public T poll() {
if (isEmpty()) {//判空
System.out.println("队列为空,无法出队");
return null; // 或者抛出异常
}
T element = array[start];
start = (start + 1) % array.length;
return element;
}
//实现迭代器遍历
public Iterator iterator(){
return new Iterator() {
int p = start;
@Override
public boolean hasNext() {
return p != start;//不循环的条件
}
@Override
public T next() {
T value = array[p];
p = (p + 1) % array.length;//后移
return value;
}
};
}
对于上述代码,你可以想想怎么优化?
1.数组长度固定(可以使用自动扩容)
2.size记录长度不需要浪费空间