我正在寻找一种数据结构,
我排除了ArrayList
,因为插入列表的开头效率不高。
表面LinkedList
应该是一个完美的选择,但实际上Java实现在插入现有元素之前或之后效率不高(即,它遍历整个列表以查找插入位置)。
(我个人不需要存储重复的元素,但其他人可能会存储)
动机:我正在建立一个事件队列,该队列允许偶尔作弊(在现有事件之前或之后插入)。
答案 0 :(得分:8)
老实说,我认为LinkedList
的自定义实现将是一种方法:
Map<?, Node>
,则可以在恒定时间内访问Node
(及其上一个/下一个节点),并以这种方式插入/删除。根据事件的总数(事件可以作弊的频率),还可以考虑线性时间,从而允许您使用API的LinkedList
实现。
答案 1 :(得分:0)
AVL树怎么样?由于树始终是平衡的,因此它的高度为O(log(N)),其中N是树中的节点数。这将导致最坏的情况 还插入O(log(N))。
我相信您所寻找的东西将尽可能接近固定的时间。抱歉,如果这不是答案,我的声誉不足,还没有发表评论:/
答案 2 :(得分:0)
我正在建立一个事件队列,该队列允许偶尔作弊(在现有事件之前或之后插入)。
如果作弊很少见,请使用您自己的基于数组的结构。 java.util.ArrayDeque
处理O(1)的两端,但不能在中间插入。可以轻松添加它们,但是复杂度为O(n)。
请注意,LinkedList
的大多数操作(由于其糟糕的数据位置)比ArrayList
和ArrayDeque
慢得多。
让我们假设,您非常需要在中间插入固定时间。像
insertAfter(existingElement, newElement);
java.util.LinkedList
的问题在于它没有这样的操作。它只有ListIterator
,它允许您在中间插入,但这仅在您首先定位迭代器(O(n))时才有用。
因此,您需要自己的链接列表。基本实现相当简单,您还需要直接访问其节点(包含next
和prev
链接的内容,而不是您add
的内容)。然后维护一个Map<MyThing, MyListNode>
,使您可以找到existingElement
的节点。现在,您只需为newElement
创建一个新节点,正确地链接它(基本上是微不足道的,但是有一些极端情况)并更新地图。
要消除边界情况(第一个/最后一个/唯一节点),请将两个虚拟节点(prefirst
和postlast
)添加到链接列表。显然,这些是不会存储在地图中的唯一节点。它们使实现变得非常简单,例如
void insertAfter(MyThing existingElement, MyThing newElement) {
Node found = map.get(existingElement);
if (found == null) throw ...
Node newNode = new Node(newElement);
map.put(newElement, newNode);
// Link newNode where it belongs to.
newNode.next = found.next;
newNode.prev = found;
// Fix links.
newNode.prev.next = newNode;
newNode.next.prev = newNode;
}
void insertFirst(MyThing newElement) {
Node found = prefirst;
// All the remaining code is simply copied from above.
// Factor it out.
Node newNode = new Node(newElement);
map.put(newElement, newNode);
// Link newNode where it belongs to.
newNode.next = found.next;
newNode.prev = found;
// Fix links.
newNode.prev.next = newNode;
newNode.next.prev = newNode;
}
答案 3 :(得分:0)
O(1)
中找到元素的。)hashCode()
和equals()
)。Map接口的哈希表和链表实现,带有 可预测的迭代顺序。此实现不同于HashMap 因为它维护了遍历其所有内容的双向链表 条目。此链表定义了迭代顺序,即 通常,将键插入地图的顺序 (插入顺序)。
我不赞成自定义集合,因为您将花费大量时间重新发明轮子,然后最终得出一个结论,那就是最好使用LinkedHashMap
。
请注意,此集合不是线程安全的。
答案 4 :(得分:-1)
我通常将c ++ 11的forward_list
用于需要在任何位置进行最佳插入的操作。它基于单链表。参见forward_list。
转发列表是序列容器,允许在序列中的任何位置进行恒定时间的插入和擦除操作。请参见forward_list的第一句话
如果您最关心的是优化,那么为什么不切换到c ++。我不确定Java中是否存在这样的功能。请注意,尽管forward_list提供恒定的时间插入,但是您不能直接通过位置访问元素。
一般来说,当您说要插入某个位置时,对于链表。 有两种情况:
您已引用该位置的节点。在这种情况下,时间复杂度为O(1)
您只有索引。时间复杂度将为O(n)