网站首页 > java教程 正文
上一篇中基于数组实现了一个普通的队列手写java数据结构(普通队列-数组)这个普通队列的出队操作时间复杂度是O(n)级别的
我们用一个容量为8的数组data来实现一个队列,此时队列中已经入队a,b,c,d,e五个元素,a处于队首位置,e处于队尾位置。此时我们将a元素进行出队操作,那么剩余的元素则需要向左移动。所以这个普通队列的出队操作时间复杂度是O(n)级别的
对于这种情况我们可能会想,我们能不能不移动剩余元素,实现一个性能更好的队列呢?
接下来我们就实现一个循环队列来解决这个问题。
我们先来分析一下如何实现一个循环队列:
仍旧以上面的图示进行分析,当队首元素a出队以后,剩余元素此时不进行移动。我们定义两个标识front 和 tail
- front:标识队首位置,如下图元素b此时为队首元素
- tail:标识下次入队时元素插入的位置,如下图下次入队时元素插入位置为索引5的位置
那么之后进行出队、入队操作后我们只需要维护front、tail就可以了。
- 出队:front往后移动一个位置
- 入队:tai往后移动一个位置
接下来我们在分析一下特殊情况:
- 初始情况下,队列中没有任何元素时,front和tail都指向同一个位置,也就是说如果front == tail队列为空
2.
如果此时再次入队一个元素h,根据我们前面说的,tail需要往后移动一个位置。而此时tail++就会造成新的tail值超出了data数组的索引范围。既然我们要实现的是循环队列,那么对于此时这种情况我们肯定要让它循环起来。我们就要判断这个数组实现的队列是否已满,如果没有满的话说明front前面有空余的位置,我们需要将tail指向front前面的位置。
tail的值我们总结出计算方式为:tail = (tail + 1) % data.length
比如对于上面的情况tail = 7,此时入队元素h,tail = (7 + 1) % 8; tail = 0
3.
如上这种情况,如果此时再次入队一个元素,tail元素再次往后移动一个位置时那么此时tail == front,而之前我们分析过tail == front时队列为空。而此时tail == front时队列是满的,这就会产生冲突。因此当(tail + 1)%data.leng == front的时候我们可以判定队列是满的,此时我们将tail位置闲置,浪费掉这个空间不再插入元素。此时,队列已满。
下面我们用代码实现一下:
public interface Queue<E> { void enqueue(E e); E dequeue(); E getFront(); int getSize(); boolean isEmpty(); } public class LoopQueue<E> implements Queue<E> { //声明一个数组data用于保存数据 private E[] data; //声明队首和队尾指针变量 private int front,tail; //size用于表示队列元素个数 private int size; //指定容量的构造函数 public LoopQueue(int capacity){ //加一是因为循环队列会浪费一个空间位置 data = (E[]) new Object[capacity + 1]; front = 0; tail = 0; size = 0; } //无参构造函数,默认容量为10 public LoopQueue() { this(10); } // 扩容方法 private void resize(int newCapacity){ //新建一个扩容后的数组 E[] newData = (E[]) new Object[newCapacity+1]; //将原数组data变量保存到新数组中,这里要注意按照队列的顺序进行保存 for(int i = 0;i < size; i++){ newData[i] = data[(i+front) % data.length]; } data = newData; front = 0; tail = size; } //入队 @Override public void enqueue(E e) { //首先判断队列是否已满,如果已满我们对其进行扩容 if((tail + 1) % data.length == front){ //扩容原来两倍 resize(getCapacity()*2); } //将新元素插入到tail的位置 data[tail] = e; //更新tail的值 tail = (tail + 1) % data.length; size++; } //出队 @Override public E dequeue() { if(isEmpty()){ throw new IllegalArgumentException("empty"); } //获取队首元素 E ret = data[front]; //内存回收 data[front] = null; //更新front值 front = (front + 1) % data.length; size--; //缩容 if(size == (getCapacity()/4) && getCapacity() / 2 != 0){ resize(getCapacity()/2); } return ret; } //查看队首元素 @Override public E getFront() { if(isEmpty()){ throw new IllegalArgumentException("empty"); } return data[front]; } //队列长度 @Override public int getSize() { return size; } //队列是否为空 @Override public boolean isEmpty() { return front == tail; } //队列容量 public int getCapacity(){ return data.length - 1; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("loopqueue:"); 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("]end"); return sb.toString(); } }
猜你喜欢
- 2024-09-11 阿里架构师剖析:Redis常用数据类型对应的数据结构
- 2024-09-11 聊聊经典数据结构HashMap,逐行分析每一个关键点
- 2024-09-11 压箱底Redis面试集-48.Redis 的 ListPack 数据结构是什么?
- 2024-09-11 JAVA进阶知识学习-day03 数据结构&List集合&Set集合
- 2024-09-11 Java数据结构面试必问:HashMap 底层实现原理分析
- 2024-09-11 Java路径-31-Java数据结构(我的世界java路径错误怎么办)
- 2024-09-11 《数据结构》第九篇、java中ArrayList源码解析
- 2024-09-11 JDK源码分析--Object(jdk1.8源码详细介绍)
- 2024-09-11 「Java数据结构」Java对象的比较(java对比两个对象属性的变化)
- 2024-09-11 动图+源码,演示Java中常用数据结构执行过程及原理
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)