闲话不说,先上题截图:
就是要实现图中要求的若干个api接口。有这么几个点,一个是要数组实现,如果发现数组不够用了,要扩容;第二个dequeue不是获取队列头的元素,而是获取尾部元素,这就相对简单,难点在要随机获取一个数,然后与尾部元素互换。如果没有这一条要求,通常从头部出队列的话,还有个数组循环利用的问题,就是指针取数组元素的模,然后用模来指向位置。迭代器的特点是随机获取元素,实现iterator接口的.next()方法的时候,要思考怎么实现随机获取。
1、首先来看看怎么设置成员变量
public class RandomQueue<Item> implements Iterable<Item> {
private Item[] array;
private Item item;
private int index;
private int arraysize; //
//构造方法
public RandomQueue(){
this(10);
this.arraysize = 10;
this.index = 0;
}
public RandomQueue(int capacity){
this.array = (Item[])new Object[capacity];
this.arraysize = capacity;
this.index = 0;
}
首先支持泛型类型。由于本题只在尾部进出元素,所以只考虑一个指针变量index,指向最后一个元素的后一位。数组的大小用arraysize,这里是创立的数组大小,要比数组的长度大,不是一个概念。然后是重载两个构造方法,一个是有初始容量的,一个是无参构造。构造函数里面设置index=0,一开始index指向array[0]。
二、是否为空以及数组扩容
//是否为空
public boolean isEmpty(){
return index == 0;
}
private void incr_size(){
this.arraysize = 2 * arraysize;
Item [] new_array = (Item[])(new Object[arraysize]);
for (int i=0; i<index; i++) { new_array[i] = this.array[i]; }
this.array = new_array;
}
isEmpty,如果index = 0 的话,表明数组中没有元素,所以回到条件index == 0;扩容的话,简单变为2倍,然后把老的数组拷贝到新的数组。对象成员变量变成新的数组。
三、入列和出列
//入列
public void enqueue(Item item){
if(index == arraysize - 1) this.incr_size();
this.array[index] = item;
this.index++;
}
//出列
public Item dequeue(){
Item element = null;
Item temp = null;
Random random = new Random();
if(index > 1){
int num = random.nextInt(index - 1);
temp = array[index - 1];
array[index - 1] = array[num];
array[num] = temp;
}
element = array[index-1];
this.index--;
return element;
}
入列需要判断index是否只比arraysize小1,如果是,就要扩容。
出列的话,需要构建一个随机对象,然后获取随机的int数,然后进行数的交换,然后把指针往前移一位,回到出列的元素。
四、获取元素个数和sample接口
int get_length(){ return index; }
Item sample(){
Item element = null;
Random random = new Random();
if(index >= 1){
int num = random.nextInt(index - 1);
element = array[num];
}
return element;
}
sample接口也是要通过随机数来获取。
五、难点:迭代器实现
迭代器的话主要是要实现两个方法,一个是 hasNext,字面意思就是还有没有下一个?另一个就是next方法,如果有下一个,那么下一个是谁。
@Override
public Iterator<Item> iterator() {
return new RandomQueueIterator();
}
private class RandomQueueIterator implements Iterator<Item>{
private int size = index;
private RandomQueue numqueue;
public RandomQueueIterator(){
numqueue = new RandomQueue();
for(int i = 0 ; i < index ; i ++) {
numqueue.enqueue(i);
}
}
@Override
public boolean hasNext() {
return size-- >= 1;
}
@Override
public Item next() {
int num = (int)numqueue.dequeue();
Item element = array[num];
return element;
}
}
因为next方法是要随机遍历,所以难点在于怎么不重复的获取随机数,所以想到本身RandomQueue有一个随机dequeue方法,可以随机出列元素。因此在内部类中构造一个RandomQueue的对象numqueue,利用这个对象的dequeue方法获取对应的数组下标。这里不能把对象的创建放到next方法里面,因为会循环调用next方法,如果放在里面,就会重复创建numqueue对象,就不合适了。
六、测试即结果
public static void main(String[] args) {
RandomQueue<Integer> randomQueue = new RandomQueue<>();
for(int i = 0 ; i < 15 ;i ++) {
randomQueue.enqueue(i);
}
for(int j: randomQueue) {
System.out.print(j +" ");
}
System.out.println();
for(int i = 0 ; i<15 ; i++)
System.out.print(randomQueue.dequeue()+ " ");
}
10 14 0 4 5 7 13 1 9 12 3 2 8 11 6
7 12 14 5 6 4 0 11 2 9 10 8 1 3 13
foreach实现随机迭代。结果和dequeue类似。证明测试是基本成功的。
这里仅仅为做题需要,代码的完备性不足。特此提示。