数据结构之队列(设计并实现一个自己的队列:循环数组+扩容策略)
来源:互联网 发布:淘宝火拼下架 编辑:程序博客网 时间:2024/06/06 17:31
1、队列介绍
队列是一种由有次序关系的数据项构成的数据结构,只能在一端(称为队尾)插入数据项,而从另一端(称为队头)移除数据项。
位于队列中队头位置的数据项称为首数据项。
2、队列实现分析
实现队列有2种方式,第一种方式是用链表来实现,第二种方式是用数组来实现。
首先我们来思考用哪种方式实现队列更高效:
队列最常用的操作:在队尾插入数据项,在队头移除数据项。
如果用链表实现,只需要定义成员变量队尾和队头,便可轻易的实现队列操作,而且也不用考虑扩容问题。
如果用数组实现,在队尾插入数据项时,可能会容量不足,需要进行扩容操作。
在队头删除数据项时,需要将后面的数据项向前移动一位,非常影响效率,当然肯定有解决方案,后面会详细介绍。
进行对比后,链表实现相对于简单、有优势。原因如下:
①插入数据项时,不用考虑扩容问题
②删除数据项时,后面的数据项不需要向前移动
然后链表实现也有缺点:
①过多的引用,处理复杂
Queue接口的实现类有链表LinkedList,也有数组实现类ArrayDeque、PriorityQueue,不推荐使用PriorityQueue。
这里暂时先用数组实现队列的数据结构。
3、队列设计
首先解决删除数据项时,需要将后面数据项向前移动的问题。
我们可以改进使用两个变量来记录队头(front)和队尾(rear)下标。这样的话每次删除队头操作时,数组后面的数据项就不用向前移动一位,只需要将队头(front)下标向后移一位。便可提升很大的效率。
当然,这样设计的话,移除数据项后,前面的空闲资源得不到利用,很浪费数组的资源空间。
我们继续改进,解决空间问题,我们可以使用循环数组,当插入数据超过最大下标时,我们将队尾(rear)下标置为0,充分利用数组空间资源。
最后就是扩容问题了,当数组被填满时,我们需要对数组进行扩容,我选择的是将原空间翻倍再加上一。
4、队列实现
①:新建类
public class IQueue<E>{}当然要使用泛型来限制队列的数据类型。
②:定义成员变量
private static final int DEFAULT_INITIAL_CAPACITY = 11;private transient Object[] queue;private int size;private int front;private int rear;DEFAULT_INITIAL_CAPACITY:用来记录默认队列容量大小。
queue:用于存储队列的数据项,使用transient关键字来关闭序列化,提高代码效率。
size:队列大小
front:队头数据项的下标
rear:队尾数据项的下标
③:构造方法
public IQueue() {// TODO Auto-generated constructor stubthis(DEFAULT_INITIAL_CAPACITY);}public IQueue(int initialCapacity) {// TODO Auto-generated constructor stubif (initialCapacity < 1) throw new IllegalArgumentException();size = 0;queue = new Object[initialCapacity];}初始化队列数据项数组和size。
④:add()方法,添加数据项到队尾
public synchronized void add(E e){if(size == queue.length){ensureCapacity(size*2+1);}if(size == 0){front = 0;rear = 0;}else{rear = nextIndex(rear);}queue[rear] = e;size++;}首先当然是判断是否还有数组空间可以使用,如果空间不足时,需要进行扩容。
然后判断是否是第一次插入数据,第一次插入数据需要初始化队头下标front和队尾下标rear。如果不是第一次插入,需要将rear向后移动一位。
既然我们使用的循环数组,当然不能直接rear++,而是使用了nextIndex()方法。
public synchronized int nextIndex(int i){return ++i==queue.length?0:i;}如果下标加一后,超过了数组最大下标值,则赋值为0。
⑤:remove()方法,移除队头数据项并返回
public synchronized E remove(){E answer;answer = (E) queue[front];queue[front] = null;front = nextIndex(front);size--;return answer;}首先获取队头的值,用于返回。
然后将其队头数据项赋值为null,帮助垃圾回收。
然后只需要将front向后移一位,使用同上方法(nextIndex()),便成功的完成了删除操作。
⑥:ensureCapacity()方法,扩容
public synchronized void ensureCapacity(int minCapacity){if(queue.length < minCapacity){if(size == 0){queue = new Object[minCapacity];}else{Object[] newQueue = new Object[minCapacity];if(front <= rear){System.arraycopy(queue, front, newQueue, 0, size);}else{int h = queue.length-front;int q = rear+1;System.arraycopy(queue, front, newQueue, 0, h);System.arraycopy(queue, 0, newQueue, h, q);}front = 0;rear = size - 1;queue = newQueue;}}}首先既然方法名叫做“确保容量”,则需要判断当前数组容量是否小于了最小需求容量(minCapacity),如果小于了最小需求容量,则说明需要扩容。
然后判断当前队列长度是否为0,如果为0,则只需要申请空间就可以了,不需要复制数据。
如果队列长度不为0,则需要复制数据。由于使用了循环数组,复制数据又出现了两种情况:
数据只使用了一段(front<=rear)
数据分为了两段(front>rear)
当然也不复杂,分为了两段后多复制一次到后面就行了。
最后都需要进行同样的操作,重新初始化front和rear,并将新队列赋值给queue。当然size是没有变化的,则不需要修改。
⑦:其他方法
public int size() {return size;}public boolean isEmpty() {return size == 0;}这就不用解释了吧。
最近比较赶时间,后面再对文章进行改进,希望对大家有帮助。
- 数据结构之队列(设计并实现一个自己的队列:循环数组+扩容策略)
- 数据结构之循环数组实现队列
- 数据结构之——基于数组实现的循环队列
- [数据结构] 队列的循环数组实现
- 【C++数据结构】数组循环队列的实现
- 队列的数组实现(循环队列)
- 数据结构代码实现(循环队列的实现,数组)
- 数据结构 -- 队列 & 循环队列 -- 数组实现
- 数据结构:队列(循环数组实现)
- 数据结构(一) -- 循环队列数组实现
- 数据结构-循环数组实现队列
- 数据结构(java语言描述)-- 队列的循环数组实现
- 简单数据结构的实现之循环队列
- 数据结构之循环队列的实现
- JAVA数据结构之循环队列的实现
- 数据结构(C实现)------- 顺序队列(循环队列之少用一个存储空间实现) .
- 《大话数据结构》读书笔记之 队列抽象数据类型(数组实现循环队列)
- 循环数组实现一个队列
- centos6.x 安装mysql linux 安装mysql
- Java的基础语法
- Hibernate的单向多对一映射
- oledb
- idhttp控件Post含中文返回值的解析
- 数据结构之队列(设计并实现一个自己的队列:循环数组+扩容策略)
- Android测试和调试的一些学习笔记
- 刹那繁华
- Linux80端口被占用tomcat服务起不来
- 【leedcode】392. Is Subsequence
- C++ 状态机
- Apicloud开发新闻类App实战项目-老孟编程
- SpringBoot项目的云服务器部署
- 文章标题