普通队列
解释高级队列前,先来看普通队列出队:
出队之后,后边所有的元素都要往前移,时间复杂度为O(n)级别,那么,如果我们能避免往前移这个操作,时间复杂度不久是O(1)了吗?
高级队列
由此设计出了一个更高级的队列:
第一个元素被移除后,把二个元素当作队首,第二个元素被移除后,把三个元素当作队首,这样就避免了所有元素往前移的操作,但是这样前边的位置就空了,空间有浪费。
如果,后边存满,我们往前边空的位置存,这样就可以解决空间浪费问题了。这就是循环队列
怎么实现呢?
我们只需要标识一下队首和队尾不就可以实现了!
看一下图示:
入队操作
出队操作
再入队:
重点来了,后边存满了,咱们该往前边空的地方存了!
如此就实现了循环队列。
但是具体到代码实现上,你会发现我们始终都多申请了一个位置,这个位置就是用来判满的。
这样我们只需要判断 (tail + 1) % data.length == front
就可以知道是否存满。
如果不留,判空、调整数组大小会变得比较复杂。感兴趣的可以自行百度。这里只讲简单实现。
代码实现
/**
* ClassName Array
* Description TODO 类描述:数组章节自己写的数组
*
* @author ZhangRenjie
* Date 2021/1/18 22:36
*/
public class Array<E> {
private E[] data;
private int size;
// 构造函数,传入数组的容量capacity构造Array
public Array(int capacity){
data = (E[])new Object[capacity];
size = 0;
}
// 无参数的构造函数,默认数组的容量capacity=10
public Array(){
this(10);
}
// 获取数组的容量
public int getCapacity(){
return data.length;
}
// 获取数组中的元素个数
public int getSize(){
return size;
}
// 返回数组是否为空
public boolean isEmpty(){
return size == 0;
}
// 在index索引的位置插入一个新元素e
public void add(int index, E e){
if(index < 0 || index > size) {
throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");
}
if(size == data.length) {
resize(2 * data.length);
}
for(int i = size - 1; i >= index ; i --) {
data[i + 1] = data[i];
}
data[index] = e;
size ++;
}
// 向所有元素后添加一个新元素
public void addLast(E e){
add(size, e);
}
// 在所有元素前添加一个新元素
public void addFirst(E e){
add(0, e);
}
// 获取index索引位置的元素
public E get(int index){
if(index < 0 || index >= size) {
throw new IllegalArgumentException("Get failed. Index is illegal.");
}
return data[index];
}
public E getLast(){
return get(size - 1);
}
public E getFirst(){
return get(0);
}
// 修改index索引位置的元素为e
public void set(int index, E e){
if(index < 0 || index >= size) {
throw new IllegalArgumentException("Set failed. Index is illegal.");
}
data[index] = e;
}
// 查找数组中是否有元素e
public boolean contains(E e){
for(int i = 0 ; i < size ; i ++){
if(data[i].equals(e)) {
return true;
}
}
return false;
}
// 查找数组中元素e所在的索引,如果不存在元素e,则返回-1
public int find(E e){
for(int i = 0 ; i < size ; i ++){
if(data[i].equals(e)) {
return i;
}
}
return -1;
}
// 从数组中删除index位置的元素, 返回删除的元素
public E remove(int index){
if(index < 0 || index >= size) {
throw new IllegalArgumentException("Remove failed. Index is illegal.");
}
E ret = data[index];
for(int i = index + 1 ; i < size ; i ++) {
data[i - 1] = data[i];
}
size --;
data[size] = null; // loitering objects != memory leak
if(size == data.length / 4 && data.length / 2 != 0) {
resize(data.length / 2);
}
return ret;
}
// 从数组中删除第一个元素, 返回删除的元素
public E removeFirst(){
return remove(0);
}
// 从数组中删除最后一个元素, 返回删除的元素
public E removeLast(){
return remove(size - 1);
}
// 从数组中删除元素e
public void removeElement(E e){
int index = find(e);
if(index != -1) {
remove(index);
}
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append(String.format("Array: size = %d , capacity = %d\n", size, data.length));
res.append('[');
for(int i = 0 ; i < size ; i ++){
res.append(data[i]);
if(i != size - 1) {
res.append(", ");
}
}
res.append(']');
return res.toString();
}
// 将数组空间的容量变成newCapacity大小
private void resize(int newCapacity){
E[] newData = (E[])new Object[newCapacity];
for(int i = 0 ; i < size ; i ++) {
newData[i] = data[i];
}
data = newData;
}
}
/**
* ClassName Queue
* Description TODO 类描述 自定义队列
*
* @author ZhangRenjie
* Date 2021/1/19 9:40
*/
public interface Queue<E> {
/**
* Description //TODO 返回队列大小
* @return int 队列大小
* @author ZhangRenJie
* @date 2021/1/19 9:42
**/
int getSize();
/**
* Description //TODO 队列是否为空
* @return boolean 是否为空
* @author ZhangRenJie
* @date 2021/1/19 9:42
**/
boolean isEmpty();
/**
* Description //TODO 入队——队末添加一个元素
* @param e 入队的元素
* @date 2021/1/19 9:42
**/
void enqueue(E e);
/**
* Description //TODO 出队-移除并返回队首元素
* @return E 队首元素
* @author ZhangRenJie
* @date 2021/1/19 9:44
**/
E dequeue();
/**
* Description //TODO 查看队首元素
* @return E 队首元素
* @author ZhangRenJie
* @date 2021/1/19 9:44
**/
E getFront();
}
/**
* ClassName LoopQueue
* Description TODO 类描述:环形队列,队首和队尾连接
*
* @author ZhangRenjie
* Date 2021/1/21 20:04
*/
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private int front, tail;
private int size;
/**
* 构造方法,根据传入大小生成队列
* @param capacity 数组大小
*/
public LoopQueue(int capacity) {
data = (E[]) new Object[capacity + 1];
front = 0;
tail = 0;
size = 0;
}
/**
* 无参构造方法,生成默认大小的队列
*/
public LoopQueue() {
this(10);
}
/**
* 返回队列容量
* @return
*/
public int getCapacity() {
return data.length - 1;
}
/**
* Description //TODO 返回队列大小
*
* @return int 队列大小
* @author ZhangRenJie
* @date 2021/1/19 9:42
**/
@Override
public int getSize() {
return size;
}
/**
* Description //TODO 队列是否为空
*
* @return boolean 是否为空
* @author ZhangRenJie
* @date 2021/1/19 9:42
**/
@Override
public boolean isEmpty() {
return front == tail;
}
/**
* Description //TODO 入队——队末添加一个元素
*
* @param e 入队的元素
* @date 2021/1/19 9:42
**/
@Override
public void enqueue(E e) {
// 判断循环队列是否存满
if ((tail + 1) % data.length == front) {
reSize(getCapacity() * 2);
}
data[tail] = e;
tail = (tail + 1) % data.length;
size++;
}
/**
* Description //TODO 调整循环队列容量
* @param newCapacity 需要的容量
* @author ZhangRenJie
* @date 2021/1/21 21:21
*/
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;
}
/**
* Description //TODO 出队-移除并返回队首元素
*
* @return E 队首元素
* @author ZhangRenJie
* @date 2021/1/19 9:44
**/
@Override
public E dequeue() {
if (isEmpty()) {
throw new IllegalArgumentException("不能从空的队列中移除元素");
}
E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size --;
if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
reSize(getCapacity() / 2);
}
return ret;
}
/**
* Description //TODO 查看队首元素
*
* @return E 队首元素
* @author ZhangRenJie
* @date 2021/1/19 9:44
**/
@Override
public E getFront() {
if (isEmpty()) {
throw new IllegalArgumentException("不能从空的队列中移除元素");
}
return data[front];
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append(String.format("Queue: size = %d, capacity = %d\n", size, getCapacity())).append("front [");
for (int i = front; i != tail ; i = (i + 1) % data.length) {
res.append(data[i]);
if ((i + 1) % data.length != tail) {
res.append(", ");
}
}
res.append("] tail");
return res.toString();
}
}