循环队列
- 本循环队列使用顺序表与双向链表实现循环队列。
- 使用junit测试完所有类的方法。
- 使用泛型使得循环队列可以容纳任意元素。
循环顺序表
数据结构:
实现方法:
public ArrayDeque()
初始化ArrayDeque队列,将nextLast,nextFirst,itemFirst都设置为相同值。public void resize(int capacity)
根据capacity调整队列大小,并将所有元素从itemFirst到最后一个元素移动到新的队列,并更新nextLast,nextFirst,itemFirst的位置信息。public void addFirst(T item)
在队列的头部添加新的元素,对于size为0特殊处理。public void addLast(T item)
在队列的尾部添加新的元素,对size为0特殊处理。public boolean isEmpty()
判断队列是否为空,为空返回true。public int size()
返回当前队列的大小,通过成员变量size避免迭代。public void printDeque()
打印当前对列中的所有元素,元素间用空格分隔,输出完毕后,输出回车。public T removeFirst()
删除当前队列头部元素,当队列的使用率小于等于0.25时,调整队列大小,当size为0特殊处理。public T removeLast()
删除当前队列尾部元素,当队列的使用率小于等于0.25时,调整队列大小,当size为0特殊处理。public T get(int index)
获取当前队列index上的元素,由于队列的头部不确定,所以通过循环查找元素位置。
public class ArrayDeque<T> {
private T[] items;
private int size;
private int RFACTOR = 2;
private int itemFirst;
private int nextFirst;
private int nextLast;
public ArrayDeque() {
items = (T[]) new Object[8];
size = 0;
nextLast = 0;
nextFirst = 0;
itemFirst = 0;
}
public void resize(int capacity) {
T[] a = (T[]) new Object[capacity];
int index = itemFirst;
for (int i = 0; i < size; i++) {
a[i] = items[index];
index = (index + 1) % items.length;
}
itemFirst = 0;
nextFirst = capacity - 1;
nextLast = size;
items = a;
}
/**
* Adds an item of type T to the front of the deque.
* @param item
*/
public void addFirst(T item) {
if (size >= items.length) {
resize(size * RFACTOR);
}
if (size == 0) {
items[itemFirst] = item;
nextFirst = (itemFirst - 1 + items.length) % items.length;
nextLast = (itemFirst + 1) % items.length;
size++;
return;
}
items[nextFirst] = item;
nextFirst = (nextFirst - 1 + items.length) % items.length;
itemFirst = (itemFirst - 1 + items.length) % items.length;
size++;
}
/**
* Adds an item of type T to the back of the deque.
* @param item
*/
public void addLast(T item) {
if (size >= items.length) {
resize(size * RFACTOR);
}
if (size == 0) {
items[itemFirst] = item;
nextFirst = (itemFirst - 1 + items.length) % items.length;
nextLast = (itemFirst + 1) % items.length;
size++;
return;
}
items[nextLast] = item;
nextLast = (nextLast +1) % items.length;
size++;
}
/**
* Returns true if deque is empty, false otherwise.
* @return
*/
public boolean isEmpty() {
if (size == 0) {
return true;
}
return false;
}
/**
* Returns the number of items in the deque.
* @return
*/
public int size() {
return size;
}
/**
* Prints the items in the deque from first to last, separated by a space. Once all the items have been printed, print out a new line.
*/
public void printDeque() {
int index = itemFirst;
for (int i = 0; i < size; i++) {
System.out.print(items[index]+" ");
index = (index + 1) % items.length;
}
System.out.println();
}
/**
* Removes and returns the item at the front of the deque. If no such item exists, returns null.
*/
public T removeFirst() {
if ((double) size / items.length <= 0.25) {
resize(items.length / RFACTOR);
}
if (size == 0) {
return null;
}
size--;
if (size == 0) {
nextFirst = itemFirst;
nextLast = itemFirst;
return items[itemFirst];
} else {
itemFirst = (itemFirst + 1) % items.length;
nextFirst = (nextFirst + 1) % items.length;
return items[nextFirst];
}
}
/**
* Removes and returns the item at the back of the deque. If no such item exists, returns null.
* @return
*/
public T removeLast() {
if ((double) size / items.length <= 0.25) {
resize(items.length / RFACTOR);
}
if (size == 0) {
return null;
}
size--;
if (size == 0) {
nextFirst = itemFirst;
nextLast = itemFirst;
return items[itemFirst];
} else {
nextLast = (nextLast - 1 + items.length) % items.length;
return items[nextLast];
}
}
/**
* Gets the item at the given index, where 0 is the front, 1 is the next item, and so forth. If no such item exists, returns null. Must not alter the deque!
* @param index
* @return item
*/
public T get(int index) {
int k = itemFirst;
for (int i = 0; i < index; i++) {
k = (k+1) % items.length;
}
return items[k];
}
}
双向循环链表
数据结构
实现方法:
public LinkedListDeque()
初始化一个空的循环双向链表队列public LinkedListDeque(LinkedListDeque other)
创建一个与other队列相同的循环队列副本。public void addFirst(T item)
在循环队列的头部添加一个新的元素。public void addLast(T item)
在循环队列的尾部添加一个新的元素。public boolean isEmpty()
判断当前循环队列是否为空。public int size()
返回当前链表的大小。public void printDeque()
打印当前循环队列的所有元素,元素之间以空格分隔,输出完毕后输出空格。public T removeFirst()
删除队列中的头部元素。public T removeLast()
删除队列中的尾部元素。public T get(int index)
获取当前队列中的index索引处的元素,通过迭代的方式。public T getRecursive(int index)
获取当前队列中的index索引出的元素,通过递归的方式。public Iterator<T> iterator()
实现Iterable接口的iterator方法,使得循环队列可以用java中foreach语法。public boolean equals(Object obj)
复写object类中的equals方法,使得循环队列有正确的比较相等操作。
import java.util.Iterator;
import java.util.NoSuchElementException;
public class LinkedListDeque<T> implements Iterable<T> {
private DequeNode first;
private int size;
public LinkedListDeque() {
first = null;
size = 0;
}
public LinkedListDeque(LinkedListDeque other) {
if (other.first == null && other.size() == 0) {
first = null;
size = 0;
}else {
first = null;
size = 0;
for (DequeNode p = other.first; p != other.first.prev; p = p.next) {
addLast(p.item);
}
addLast((T) other.first.prev.item);
}
}
private class DequeNode {
private T item;
private DequeNode prev;
private DequeNode next;
DequeNode(T i, DequeNode p, DequeNode n) {
item = i;
prev = p;
next = n;
}
}
/**
* Adds an item of type T to the front of the deque.
* @param item
*/
public void addFirst(T item){
if (first == null) {
first = new DequeNode(item, null, null);
first.next = first;
first.prev = first;
} else {
first = new DequeNode(item, first.prev, first);
first.next.prev = first;
}
size++;
}
/**
* Adds an item of type T to the back of the deque.
* @param item
*/
public void addLast(T item){
if (first == null) {
first = new DequeNode(item, null, null);
first.next = first;
first.prev = first;
} else {
first.prev = new DequeNode(item, first.prev,first);
first.prev.prev.next = first.prev;
}
size++;
}
/**
* Returns true if deque is empty, false otherwise.
* @return
*/
public boolean isEmpty(){
if (size == 0) {
return true;
} else {
return false;
}
}
/**
* Returns the number of items in the deque.
* @return
*/
public int size(){
return size;
}
/**
* Prints the items in the deque from first to last, separated by a space. Once all the items have been printed, print out a new line.
*/
public void printDeque(){
for (DequeNode p = first; p != first.prev; p= p.next) {
System.out.print(p.item+" ");
}
System.out.print(first.prev.item + " ");
System.out.println();
}
/**
* Removes and returns the item at the front of the deque. If no such item exists, returns null.
*/
public T removeFirst(){
if (isEmpty()) {
return null;
}
size--;
DequeNode p = first;
first.prev.next = first.next;
first.next.prev = first.prev;
first = first.next;
return p.item;
}
/**
* Removes and returns the item at the back of the deque. If no such item exists, returns null.
* @return
*/
public T removeLast(){
if (isEmpty()) {
return null;
}
size--;
DequeNode p = first.prev;
first.prev.prev.next = first;
first.prev = first.prev.prev;
return p.item;
}
/**
* Gets the item at the given index, where 0 is the front, 1 is the next item, and so forth. If no such item exists, returns null. Must not alter the deque!
* @param index
* @return item
*/
public T get(int index){
if (index < 0 || index >= size) {
return null;
}
DequeNode p = first;
for (int i = 0; i < index; i++) {
p = p.next;
}
return p.item;
}
private T getRecursiveHelp(int index, DequeNode p) {
if (index == 0) {
return p.item;
}
return getRecursiveHelp(index-1, p.next);
}
/**
* Gets the item at the given index, where 0 is the front, 1 is the next item, and so forth. If no such item exists, returns null. Must not alter the deque!
* @param index
* @return
*/
public T getRecursive(int index) {
if (index < 0 || index >= size) {
return null;
}
return getRecursiveHelp(index, first);
}
private class DequeIterator implements Iterator<T>{
private DequeNode current = first;
int i = 0;
@Override
public boolean hasNext() {
if (i < size) {
i++;
return true;
}
return false;
}
@Override
public T next() {
T item = current.item;
current = current.next;
return item;
}
}
@Override
public Iterator<T> iterator() {
return new DequeIterator();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof LinkedListDeque)) return false;
LinkedListDeque<?> other = (LinkedListDeque<?>) obj;
if (this.size != other.size) return false;
Iterator<T> thisIt = this.iterator();
Iterator<?> otherIt = other.iterator();
while (thisIt.hasNext() && otherIt.hasNext()) {
if (!thisIt.next().equals(otherIt.next())) {
return false;
}
}
return true;
}
}
循环队列实现的对比
循环链表队列(LinkedListDeque)
优点:
动态扩展: 链表队列可以根据需要动态地增加节点,不需要事先分配固定的存储空间。
内存利用: 在任何时候,链表都只占用它实际需要的内存空间。
复杂度: 在链表的头部和尾部添加或删除元素都是常数时间的操作(O(1)),因为不需要移动其他元素。
缺点:
内存开销: 每个元素需要额外存储指针信息,这增加了内存的开销。
时间开销: 访问链表中间的元素需要从头节点或尾节点遍历,因此访问时间为线性时间(O(n))。
缓存不友好: 链表的节点可能在内存中不连续,导致缓存命中率降低,可能影响性能。
循环数组队列(ArrayDeque)
优点:
随机访问: 由于数据连续存储,支持快速随机访问,访问任意元素的时间复杂度为O(1)。
内存连续: 数据存储在连续的内存空间中,有利于提高缓存的利用率。
空间预分配: 初始时即分配一个数组,可以减少内存的频繁分配。
缺点:
固定容量: 初始需要定义数组的大小,虽然可以动态扩容,但这是通过创建新数组并复制旧数组元素来实现的,耗费时间和空间。
扩容成本: 扩容操作涉及到内存的重新分配及数据的复制,特别是当队列大小达到数组容量时,每次添加元素都可能触发扩容,这可能导致较大的性能开销。
可能的空间浪费: 如果队列的使用量远小于数组的实际分配大小,会造成空间浪费。