Algorithms课程里留了一个作业:用数组实现可自动扩充的队列数据结构。已经给了具体思路,但是实现还遇到了一些问题。
按照Stack的思路照搬,申请一个固定大小(capacity)的数组,用head和tail来指向头和尾。head++表示出队,tail++表示入队。为了方便,我把tail指向尾部的下一个,当head==tail是就为空。实现Stack时为了高效扩充,当超出capacity时将它扩充一倍(申请新数组复制原来的元素),出队时如果数据只占capacity的1/4则缩小一半。这样避免了最坏情况下频繁的扩大和缩小带来的空间和时间消耗。
head如果后移,那么它前面的空间可以再次利用。因此,当tail超出数组时可以移到开头。按照这样的想法,很容想到当tail大于capacity时将其移动到开头,然后根据tail和head的位置来索引队列。按照tail和head的先后一定能够做到啊。但事实是不行,如果仅仅把tail和head的值限制在capacity内时就不行了,比如,如果tail和head碰到了一起那是满了呢还是为空呢?而且,每个操作还有很复杂的判断,当最后知道不可以了后就想放弃这个问题了。但马上灵光乍现,不必要限制tail和head的大小啊,可以通过取其余数来索引啊。于是,问题变得和Stack的实现一样简单。仅仅多了索引的时候记得取余数来索引就好了。
写完碰到了一个小问题没注意,于是一直出错。写resize函数时偷懒,用foreach方式来遍历自己。结果遍历之前先修改了head和tail的值(resize将内容复制到新数组,因此要调整head和tail值),使得依赖这两个值的遍历没法正常完成。好在最后通过多次打印调试发现了这个问题。
附上未经修改为generics的源码,为了调试有的地方还改得麻烦了一点。
···
import java.util.Iterator;
import java.util.NoSuchElementException;
public class ArrayQueue implements Iterable
private int[] list;
private int head,tail;
private int capacity;
@Override
public Iterator<Integer> iterator() {
return new ListIterator();
}
private class ListIterator implements Iterator<Integer>{
private int current = head-1;
@Override
public boolean hasNext() {
return current != tail - 1;
}
@Override
public Integer next() {
current++;
if(current < tail) {
return list[idx(current)];
}
throw new NoSuchElementException();
}
}
private void init() {
list = new int[capacity];
head = tail = 0;
}
public ArrayQueue(int capacity) {
this.capacity = capacity;
init();
}
public ArrayQueue() {
this.capacity = 10;
init();
}
public int size() {
return tail - head;
}
public boolean isEmputy() {
return size() == 0;
}
public void enqueue(int data) {
if(size()+1 > capacity) {
resize(capacity*2);
}
list[idx(tail++)] = data;
}
public int dequeue() throws Exception {
if(isEmputy())
throw new Exception("Emputy Queue");
if(size()-1 < capacity/4) {
resize(capacity/2);
}
int item = list[idx(head++)];
return item;
}
public String toString() {
String ret = "";
for( int v : this) {
ret += v + ", ";
}
ret += "capacity="+capacity;
return ret;
}
private void resize(int capacity) {
int[] old_list = this.list;
int[] list = new int[capacity];
int sz = size();
Iterator<Integer> iter = this.iterator();
for(int i=0; iter.hasNext(); i++) {
list[i] = iter.next();
}
head = 0;
tail = head+sz;
this.list = list;
this.capacity = capacity;
}
private int idx(int index) {
return index % capacity;
}
public static void main(String[] args) throws Exception {
ArrayQueue que = new ArrayQueue();
for(int i =1; i<=10; i++) {
que.enqueue(i);
}
System.out.println(que.toString());
que.enqueue(11);
que.enqueue(12);
System.out.println(que.toString());
que.dequeue();
que.dequeue();
que.dequeue();
que.dequeue();
que.dequeue();
que.dequeue();
que.dequeue();
System.out.println(que.toString());
que.dequeue();
System.out.println(que.toString());
que.dequeue();
System.out.println(que.toString());
que.dequeue();
System.out.println(que.toString());
que.dequeue();
System.out.println(que.toString());
}
}
···