我正在尝试实现来自www.topcoder.com的挑战的数据结构,但是我无法获得与他们相同的速度/效率。
这就是他们的要求。
问题:
实现一个实现List接口的BlockedList类。您可以使用JCF中的任何类。此类的构造函数采用整数块大小b,并且实现应具有以下性能特征: 每个操作get(i)和set(i,x)应在O(1)时间内运行 每个操作的add(i,x)和remove(i)应以O(b + min {i,n-i} / b)的摊销时间运行。
解决方案(不正确,应该更快一些:()
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
/**
* This is a copy of the JCF class ArrayList. It implements the List
* interface as a single array a. Elements are stored at positions
* a[0],...,a[size()-1]. Doubling/halving is used to resize the array
* a when necessary.
*
*
* @param <T> the type of objects stored in the List
*/
class BlockedList<T> extends AbstractList<T> {
/**
* List which will store elements
*/
private List<T> list;
/**
* keeps track of the class of objects we store
*/
Factory<T> f;
/**
* The number of elements stored
*/
int n;
/**
* The block size
*/
int b;
/**
* Constructor
* @param t the type of objects that are stored in this list
* @param b the block size
*/
public BlockedList(Class<T> t, int b) {
this.f = new Factory<T>(t);
this.n = 0;
this.b = b;
this.list = new ArrayList<T>();
}
public int size() {
return n;
}
public T get(int i) {
if (i < 0 || i > n - 1) throw new IndexOutOfBoundsException();
return list.get(i);
}
public T set(int i, T x) {
if (i < 0 || i > n - 1) throw new IndexOutOfBoundsException();
return list.set(i, x);
}
public void add(int i, T x) {
if (i < 0 || i > n) throw new IndexOutOfBoundsException();
if (i >= b ) throw new IndexOutOfBoundsException();
list.add(i,x);
n+=1;
}
public T remove(int i) {
if (i < 0 || i > n - 1) throw new IndexOutOfBoundsException();
T val = list.remove(i);
n-=1;
return val;
}
}
目标
目标是按照他们的要求完成挑战,但是我已经放弃了多次尝试。我想知道我做错了什么。同样,我之前提到的get(i)和set(i,x)的速度应该在每次操作的O(1)时间中运行 每个操作的add(i,x)和remove(i)应以O(b + min {i,n-i} / b)的摊销时间运行。
测试错误:
Executing: javac ListTest2.java
Running: java ListTest2 20
Running with n = 20
Correctness testing for topcoder.BlockedList...Exception in thread "main" java.lang.IndexOutOfBoundsException
at topcoder.BlockedList.checkBounds(BlockedList.java:67)
at topcoder.BlockedList.add(BlockedList.java:43)
at java.util.AbstractList.add(AbstractList.java:108)
at topcoder.sanityTests(ListTest2.java:30)
at topcoder.main(ListTest2.java:115)
Program exited with non-zero status, test failed
答案 0 :(得分:0)
它实际上并没有进行任何阻止。您应该将 n > b 元素存储在 b 元素的块中。您的实现只是拒绝支持 n > b 。
实际上,经过反思,我没有回答“为什么这么慢?”的问题。但这似乎仅次于“它不满足要求”。
答案 1 :(得分:0)
尝试类似java.util.Map<Intger, T[]> blocks = new HashMap<>();
之类的东西。
并且做得比:
if (i >= b ) throw new IndexOutOfBoundsException();
完整的来源(未经测试):
import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Random;
import java.util.HashMap;
public class BlockedList<T> extends AbstractList<T> {
static class Factory<T> {
private final Class<T> clazz;
public Factory(Class<T> clazz) {
this.clazz = clazz;
}
public T[] createBlock(int b) {
return (T[]) Array.newInstance(clazz, b);
}
}
private int n;
private final Map<Integer, T[]> blocks;
private final int b;
private final Factory<T> f;
public BlockedList(Class<T> tclass, int b) {
this.f = new Factory<>(tclass);
this.n = 0;
this.b = b;
this.blocks = new HashMap<>();
}
@Override
public int size() {
return n;
}
public long capacity() {
return blocks.size() * b;
}
@Override
public void clear() {
blocks.clear();
n = 0;
}
@Override
public T get(int i) {
checkBounds(i);
return blocks.get(i / b)[i % b];
}
@Override
public T set(int i, T x) {
checkBounds(i);
final T[] block = blocks.get(i / b);
final T retVal = block[i % b];
block[i % b] = x;
return retVal;
}
@Override
public void add(int i, T x) {
checkBoundsAdd(i);
if (blocks.containsKey(i / b)) {
blocks.get(i / b)[i % b] = x;
} else {
T[] tmp = f.createBlock(b);
tmp[i % b] = x;
blocks.put(i / b, tmp);
}
n++;
}
@Override
public T remove(int i) {
checkBounds(i);
T val = blocks.get(i / b)[i % b];
if (val != null) {
n--;
}
return val;
}
private void checkBounds(int i) {
if (i < 0 || i > n - 1) {
throw new IndexOutOfBoundsException();
}
}
private void checkBoundsAdd(int i) {
if (i < 0 || i > n) {
throw new IndexOutOfBoundsException();
}
}
public static void main(String[] args) {
test();
}
private static void test() {
Random rnd = new Random();
BlockedList<Integer> test = new BlockedList<>(Integer.class, 8);
for (int i = 0; i < Short.MAX_VALUE - 7; i++) {//Integer.MAX_VALUE takes toooo long
test.add(i, rnd.nextInt());
}
System.out.println(test.size()); //Short.MAX_VALUE - 7
for (int i = 0; i < Short.MAX_VALUE - 107; i++) {
test.remove(rnd.nextInt(test.size()));
}
System.out.println(test.size()); //expect: 100
test.forEach((_item) -> {
test.set(rnd.nextInt(test.size()), rnd.nextInt(1024));
});
System.out.println(test.size()); //expect: 100
System.out.println(Arrays.toString(test.toArray()));
test.set(0, 0);
System.out.println(Arrays.toString(test.toArray()));
Collections.sort(test);
//test.toArray() uses test.get(int)
System.out.println(Arrays.toString(test.toArray()));
}
}
...现在,我们必须决定“最佳”地图实现(我在TreeMap
和HashMap
之间摇摆)。
到目前为止,我们最多有n / b
个条目/块,每个块有b
个保留大小,使用HashMap几乎可以满足复杂性要求:
O(1)
(来自HashMap.get)+ O(1)
(来自数组get)。
java.util.TreeMap
,它将是O(log(2, n/b) + 1)
。O(1 + 1)
。
TreeMap
:O(log(2, n / b) + 1)
也更昂贵。O(n / b + b)
。
O(log(2, n / b) + b)
(那会更好!)O(n / b + b)
O(log(2, n / b) + b
。 (更好)实际上是O(min(i, n - i)) == O(n / 2) == O(n)
!!
那么HashMap
用于更快的获取和设置操作,TreeMap
用于更快地添加和删除替代方法? (LinkedHashmap吗?...)