我的目标是创建一个实现IList<T>
接口的数据结构,通过破坏内存来实现O(1)
元素查找时间。
背景
如您所知,IList<T>
所有基于数组的List<T>
实现都有O(n)
个元素查找时间。这意味着像int IndexOf(T element)
或bool Contains(T element)
这样的操作会遍历底层数组,直到找到匹配为止。
众所周知的想法是使用列表和散列表的组合作为底层数据结构。值保存在列表中。哈希表将索引作为键的值和值保存。因此可以使用哈希表执行查找。
这正是KeyedCollection<TKey, TItem>
see MSDN的实施方式。
到目前为止我尝试了什么
internal class MyList<T> : KeyedCollection<T, T>
{
protected override T GetKeyForItem(T item)
{
return item;
}
}
除了一个问题外,这个目前还有效。此数据结构不能完全模仿List<T>
后面预期的行为。关键是List<T>
允许重复,MyList
不允许重复。
问题
是否有任何可以使用的数据结构,或者您是否可以推荐实现IList<T>
的优雅方式,以便:
O(1)
。O()
List<T>
constantA + constantB * n
字节)的影响。答案 0 :(得分:4)
我能看到的唯一方法是使用列表字典。点击密钥会为您提供创建该特定密钥的所有重复项的列表。总是拿第一个。
答案 1 :(得分:2)
基于Ryan Bennett
提出的建议,我认为你要提出的最好的(因为你说明顺序很重要)是创建一个实现IList的类,然后在内部有类似的东西:< / p>
class MyList<T> : IList<T>
{
Dictionary<T, List<int>> _indexMap;
List<T> _items;
public int IndexOf(T item)
{
List<int> indices;
if(_indexMap.TryGetValue(item, out indices))
{
return indices[0];
}
return -1;
}
public void Add(T item)
{
List<int> indices;
if(!_indexMap.TryGetValue(item, out indices))
{
indices = new List<int>();
_indexMap[item] = indices;
}
indices.Add(_items.Count);
_items.Add(item);
}
// Attempt at a Remove implementation, this could probably be improved
// but here is my first crack at it
public bool Remove(T item)
{
List<int> indices;
if(!_indexMap.TryGetValue(item, out indices))
{
// Not found so can just return false
return false;
}
int index = indices[0];
indices.RemoveAt(0);
if (indices.Count == 0)
{
_indexMap.Remove(item);
}
for(int i=index+1; i < _items.Count; ++i)
{
List<int> otherIndexList = _indexMap[_items[i]];
for(int j=0; j < otherIndexList.Count; ++j)
{
int temp = otherIndexList[j];
if (temp > index)
{
otherIndexList[j] = --temp;
}
}
}
return _items.RemoveAt(index);
}
// ... Other similar type functions here
}
编辑:
刚才意识到当你做Remove
时事情变得非常粘。您必须遍历索引集合并使用值&gt;更新任何索引。您删除的项目的索引。您现在已经增加了“删除”时间。你也弄清楚了。如果你打算尝试实现这样的东西,我会在这个集合周围进行大量的单元测试。
我知道你说的顺序很重要所以我假设这就是为什么你不会使用排序列表方法,它允许重复并给你O(log n)操作时间。
编辑2:另一种簿记类型方法
我只是在脑子里蹦蹦跳跳,所以我只会给出一些粗略的伪代码,但你可能会采用一种方法,你只需要一个映射到索引列表的项目字典和一个将索引映射到项目的第二个字典。如果您添加T是一个类的限制,那么您只需支付两个存储参考的开销。然后,您需要保持当前的“最后”,以便您可以轻松地将新项目添加到集合中。这应该使删除操作更清洁。它仍然是O(n),因为你必须用索引&gt;更新任何东西。删除的项目。在最初的想象中,这似乎是一个潜在的解决方案,可以让你接近你想要实现的目标(如果我正确理解目标)。
答案 2 :(得分:1)
哈希表应该包含每个键的索引列表。而且我认为这就是你所需要的,不是吗?
答案 3 :(得分:0)