如何为用户分配唯一ID,以便在已分配时尝试分配ID + 1

时间:2018-03-11 20:35:58

标签: algorithm data-structures set

用户选择他想要的ID,如果已经分配给另一个用户,则分配最大的自由ID,大于所选。

可以删除用户并使id再次可用。

我认为使用HashSet就足够了,因为它的访问复杂性是O(1),我只是通过使用contains方法检查每个更大的条目。 但是,如果分配了20000个第一个ID,然后20000个新用户尝试使用id 1,则HashSet的性能确实很慢。

我可以做些什么来提高性能?这种情况是否有特殊的数据结构?

3 个答案:

答案 0 :(得分:0)

你需要一个搜索树(或类似的东西)!二叉树是最简单的搜索树类型之一,但它的主要缺点是它不平衡,并且可能比自平衡树慢,例如红黑树。

当然,你不会有O(1)时间复杂度(我怀疑有一种算法可以找到,删除和添加O(1)),但你会O(lg n) (在最慢的树的情况下)或摊销O(1)(对于最好的树)插入和移除的时间复杂度(查找)(时间复杂度是树特定的)。

答案 1 :(得分:0)

我建议使用二叉搜索树来存储未使用的ID。

二进制搜索树支持在O(log n)时间内查找大于输入项的最小项。 (n是树中的项目数)

有些语言已经实现了这一点。例如,对于在cpp中设置的upper_bound [1]或在java中为TreeSet设置的更高[2]。

  1. http://en.cppreference.com/w/cpp/container/set/upper_bound
  2. https://docs.oracle.com/javase/7/docs/api/java/util/TreeSet.html#higher(E)

答案 2 :(得分:0)

当我问这个问题时,我并不知道,但这是SPOJ的一项任务:BLUNIQ

我通过使用范围结构和TreeSet来存储范围来解决它。所以使用搜索树的初步想法很好我只需要在范围内存储数字并正确管理它们。 TreeSet具有有用的方法,例如floor(低或相等)和更高,因此搜索两个最近范围(左侧和右侧)的复杂度为O(logN)。

来自Petar PetrovicWhatever的答案都可以,但如果没有特定的数据结构,他们会给出超出时间限制的错误。

我还没有为此任务阅读任何解决方案,因此可能会有更快的解决方案。

static class Range implements Comparable<Range> {
    int leftBound;
    int rightBound;

    public Range(int leftBound, int rightBound) {
        this.leftBound = leftBound;
        this.rightBound = rightBound;
    }

    public void remove(int x) {
        if (x == leftBound) leftBound++; else
        if (x == rightBound) rightBound--; else {
            ranges.remove(this);
            ranges.add(new Range(leftBound, x - 1));
            ranges.add(new Range(x + 1, rightBound));
        }
        if (size() <= 0) ranges.remove(this);
    }

    public long size() {
        return rightBound - leftBound + 1;
    }

    @Override
    public int compareTo(Range range) {
        return Long.compare(rightBound, range.rightBound);
    }

    public boolean contains(int x) {
        return leftBound <=x && x <= rightBound;
    }

    @Override
    public String toString() {
        return "[" + leftBound + "; " + rightBound + "]";
    }

}

static public void add(int x) {

    Range singleRange = new Range(x, x);
    Range lowerOrEqual = ranges.floor(singleRange);
    Range higher = ranges.higher(singleRange);
    if ((lowerOrEqual == null || lowerOrEqual.rightBound + 1 < x) && (higher == null || higher.leftBound - 1 > x)) {
        System.out.println(x);
        ranges.add(singleRange);
    } else {

        if (lowerOrEqual != null && (lowerOrEqual.rightBound == x || lowerOrEqual.rightBound + 1 == x)) {
            System.out.println(++lowerOrEqual.rightBound);
            if (higher != null && lowerOrEqual.rightBound == higher.leftBound - 1) {
                Range merged = new Range(lowerOrEqual.leftBound, higher.rightBound);
                ranges.remove(lowerOrEqual);
                ranges.remove(higher);
                ranges.add(merged);
            }
        } else if (higher != null && higher.leftBound - 1 == x) {
            System.out.println(--higher.leftBound);
            if (lowerOrEqual != null && lowerOrEqual.rightBound == higher.leftBound - 1) {
                Range merged = new Range(lowerOrEqual.leftBound, higher.rightBound);
                ranges.remove(lowerOrEqual);
                ranges.remove(higher);
                ranges.add(merged);
            }
        } else if (higher != null && higher.contains(x)) {
            System.out.println(++higher.rightBound);
            Range higherThanHigher = ranges.higher(higher);
            if (higherThanHigher != null && higher.rightBound + 1 == higherThanHigher.leftBound) {
                Range merged = new Range(higher.leftBound, higherThanHigher.rightBound);
                ranges.remove(higher);
                ranges.remove(higherThanHigher);
                ranges.add(merged);
            }
        }
    }
}

以及如何使用它:

private static TreeSet<Range> ranges = new TreeSet<>();

public static void main(String[] args) throws Exception {
    BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in));

    int N = Integer.parseInt(bufferRead.readLine());

    for (int i = 0; i < N; i++) {
        String[] strings = bufferRead.readLine().split(" ");
        int[] event = {Integer.parseInt(strings[0]), Integer.parseInt(strings[1])};

        if (event[0] == 1) {
            add(event[1]);
        } else {
            Range singleRange = new Range(event[1], event[1]);
            Range lowerOrEqual = ranges.floor(singleRange);
            Range higher = ranges.higher(singleRange);
            if (lowerOrEqual != null && lowerOrEqual.rightBound == event[1]) {
                lowerOrEqual.remove(event[1]);
            } else if (higher != null && higher.leftBound <= event[1]) {
                higher.remove(event[1]);
            }
        }
    }
}