0

我知道由于各种概念上的原因,Java 没有排序列表,但考虑到我需要一个类似于优先级队列但也允许我随机访问(可索引)的集合的情况,换句话说,我需要一个遵循特定顺序的列表。我宁愿使用Collections.sort()

优选的操作约束:

检索 - O(1)(基于索引的随机访问)
搜索 - O(log n)
插入 - O(log n)
删除 - O(log n)

集合上的迭代器应该给我排序顺序中的所有元素(基于在Comparator数据结构实例化期间提供的预定义)

我更喜欢使用 Java 的内置库来实现这一点,但也可以随意建议外部库。

编辑: TreeSet 不会做,因为基于索引的访问很困难,使用包装器集合也不是我的最佳选择,因为删除意味着我需要从两个集合中删除。

EDIT2:我找不到indexable skip list这似乎有点相关的实现和/或文档,有人可以帮我找到它吗?也欢迎对所提出的数据结构提出任何意见或反对意见。

EDIT3:虽然这可能不是最完美的答案,但我想添加我编写的这段代码,以便任何有类似问题需要排序列表的人都可以在发现它有用时使用它。

检查错误(如果有的话),并提出改进建议(尤其是sortedSubList方法)

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;

public class SortedList<E> extends ArrayList<E> {
    private final Comparator<? super E> comparator;

    public SortedList(Comparator<? super E> comparator) {
        this.comparator = comparator;
    }

    public SortedList(int initialCapacity, Comparator<? super E> comparator) {
        super(initialCapacity);
        this.comparator = comparator;
    }

    @Override
    public boolean add(E e) {
        if (comparator == null)
            return super.add(e);
        if (e == null)
            throw new NullPointerException();
        int start = 0;
        int end = size() - 1;
        while (start <= end) {
            int mid = (start + end) / 2;
            if (comparator.compare(get(mid), e) == 0) {
                super.add(mid, e);
                return true;
            }
            if (comparator.compare(get(mid), e) < 0) {
                end = mid - 1;
            }
            else {
                start = mid + 1;
            }
        }
        super.add(start, e);
        return true;
    }

    @Override
    public boolean contains(Object o) {
        if (comparator == null)
            return super.contains(o);
        if (o == null)
            return false;
        E other = (E) o;
        int start = 0;
        int end = size() - 1;
        while (start <= end) {
            int mid = (start + end) / 2;
            if (comparator.compare(get(mid), other) == 0) {
                return true;
            }
            if (comparator.compare(get(mid), other) < 0) {
                end = mid - 1;
            }
            else {
                start = mid + 1;
            }
        }
        return false;
    }

    @Override
    public int indexOf(Object o) {
        if (comparator == null)
            return super.indexOf(o);
        if (o == null)
            throw new NullPointerException();
        E other = (E) o;
        int start = 0;
        int end = size() - 1;
        while (start <= end) {
            int mid = (start + end) / 2;
            if (comparator.compare(get(mid), other) == 0) {
                return mid;
            }
            if (comparator.compare(get(mid), other) < 0) {
                end = mid - 1;
            }
            else {
                start = mid + 1;
            }
        }
        return -(start+1);
    }

    @Override
    public void add(int index, E e) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> c) {
        throw new UnsupportedOperationException();
    }

    @Override
    public E set(int index, E e) {
        throw new UnsupportedOperationException();
    }

    public SortedList<E> sortedSubList(int fromIndex, int toIndex) {
        SortedList<E> sl = new SortedList<>(comparator);
        for (int i = fromIndex; i < toIndex; i++)
            sl.add(get(i));
        return sl;
    }
}
4

3 回答 3

1

在同一数据结构中很难获得 O(1) 索引和 O(log n) 插入/删除。O(1) 索引意味着我们无法承担索引树、列表、跳过列表或其他基于链接的数据结构所涉及的链接跟踪,而 O(log n) 修改意味着我们无法承担一半的移位每次插入时数组的元素。我不知道是否有可能同时满足这些要求。

如果我们放宽这些要求之一,事情就会变得容易得多。例如,所有操作的 O(log n) 可以通过可索引的跳过列表或自平衡 BST 来实现,其中的节点跟踪以该节点为根的子树的大小。但是,这些都不能建立在 Java 标准库中的跳过列表或 BST 之上,因此您可能需要安装另一个库或编写自己的数据结构。

O(1) 索引、O(log n) 搜索以及 O(n) 插入和删除可以通过保持排序的 ArrayList 并Collections.binarySearch用于搜索元素或插入/删除位置来完成。您永远不需要调用Collections.sort,但您仍然需要调用 ArrayList 的 O(n) 插入和删除方法。这可能是在 Java 的内置工具之上构建的最简单的选择。请注意,对于最近的 Java 版本,Collections.sort它是一种自适应合并排序,它需要O(n)时间来对只有最后一个元素未排序的数组进行排序,因此您可能会摆脱依赖Collections.sort. 但是,这是替代 Java 实现不必遵循的实现细节。

于 2015-10-24T09:28:21.320 回答
1

如果您的主要目标是索引查找 () 的 O(1) get(),那么您可以实现自己的类 implementation List,由数组支持,使用Arrays.binarySearch().

retrieve: get(int)         - O(1)     - array index
search:   contains(Object) - O(log n) - binarySearch
          indexOf(Object)  - O(log n) - binarySearch
insert:   add(E)           - O(n)     - binarySearch + array shift
delete:   remove(int)      - O(n)     - array shift
          remove(Object)   - O(n)     - binarySearch + array shift

add(E)方法违反List定义(追加),但与Collection定义一致。

以下方法应该抛出UnsupportedOperationException

add(int index, E element)
addAll(int index, Collection<? extends E> c)
set(int index, E element)

如果不允许重复值,这可能是一个逻辑限制,请考虑实施NavigableSet,这是一个SortedSet.

于 2015-10-24T09:42:51.293 回答
0

构建一个由 ArrayList 和 TreeSet 支持的自定义集合。将随机访问委托给 ArrayList 并将搜索委托给 TreeSet。当然,这意味着每次写入操作都会非常昂贵,因为每次都必须对 ArrayList 进行排序。但是读取应该非常有效。

于 2015-10-24T09:18:39.807 回答