为什么我的数据结构慢(自定义数据结构)

时间:2019-02-15 02:13:08

标签: java data-structures

我正在尝试实现来自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

2 个答案:

答案 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()));
    }
}

...现在,我们必须决定“最佳”地图实现(我在TreeMapHashMap之间摇摆)。

到目前为止,我们最多有n / b个条目/块,每个块有b个保留大小,使用HashMap几乎可以满足复杂性要求:

  • get:O(1)(来自HashMap.get)+ O(1)(来自数组get)。
    • 使用java.util.TreeMap,它将是O(log(2, n/b) + 1)
  • 设置:O(1 + 1)
    • 使用TreeMapO(log(2, n / b) + 1)也更昂贵。
  • 添加:O(n / b + b)
    • TreeMap(我想是):O(log(2, n / b) + b)(那会更好!)
  • 删除:O(n / b + b)
    • TreeMap:O(log(2, n / b) + b。 (更好)

实际上是O(min(i, n - i)) == O(n / 2) == O(n) !!

那么HashMap用于更快的获取和设置操作,TreeMap用于更快地添加和删除替代方法? (LinkedHashmap吗?...)