我可以在C#中使用哪种数据结构来快速插入/删除以及统一随机选择? List按元素缓慢删除(因为它需要每次都找到元素的索引),而HashSet似乎不允许随机选择元素(不复制到列表。)
数据结构将不断更新,因此插入和删除需要是在线程序。似乎应该有一种方法可以进行插入,删除和随机选择所有O(log n)。
分配给对象的任意整数键的二叉搜索树可以解决所有这些问题,但是我在C#标准库中找不到合适的类。有没有一种规范的方法可以在不编写自定义二叉搜索树的情况下解决这个问题?
答案 0 :(得分:2)
C#BCL中已经有一个BST,它被称为SortedDictionary<TKey, TValue>
,如果你不想要Key Value Pairs,而是想要单个项目,你可以使用SortedSet<T>
(SortedSet是在.NET 4.0中。
听起来你的例子中你想要SortedDictionary<int, WhateverValueType>
。虽然当你说“统一随机选择”时,我不确定你到底是什么。
当然,Dictionary<TKey, TValue>
是O(1),速度要快得多。因此,除非您需要按键的排序顺序,否则我会使用它。
更新:根据您的需求,您将获得效率方面的优势。为了能够跳转到数据结构中的随机连续索引,您将多久插入/删除一次?如果不经常,你可以使用一个数组,然后在(O(n log n))之后只使用Sort(),或者总是按顺序插入/删除(O(n))。
或者,您可以打包Dictionary<int, YourType>
并保持并行List<int>
并在每次添加/删除后更新它:
_dictionary.Add(newIndex, newValue);
_indexes.Add(newIndex);
然后只需在查找列表中访问随机索引。好的是,在这个方法中,Add()实际上是~O(1)(除非List调整大小,但你可以设置一个初始容量以避免其中一些)但是你会在删除时产生O(n)成本
我担心问题是你要么在查询上牺牲次数,要么在删除/插入时牺牲。问题是所有最好的访问时间容器都是非连续的。但是,使用双List<int>/Dictionary<int, YourValue>
组合,你可以有很好的组合。
更新2 :听起来我们继续讨论如果绝对性能是您的要求,那么您可能会有更好的运气。考虑一下很有意思,如果我想到其他任何事情,我会更新。
答案 1 :(得分:2)
二元搜索树和派生结构(如SortedDictionary
或SortedSet
)通过比较键来操作。
您的对象本身不具有可比性,但它们提供对象标识和哈希值。因此,HashSet
是正确的数据结构。注意:Dictionary<int,YourType>
不合适,因为删除变为线性搜索(O(n)),并且在删除后无法解决随机问题。
RandomElement是O(n)。它可以很容易地实现,例如
set.ElementAt(random.Next(set.Count))
不需要复制到中间列表。
答案 2 :(得分:1)
我意识到这个问题已经超过3年了,但仅适用于遇到此页面的人:
如果您不需要对数据集中的项目进行排序,则可以使用List<ItemType>
。
插入和随机选择是O(1)。只需将最后一个项目移动到要删除的项目的位置并从末尾删除它,即可在O(1)中删除。
代码:
using System; // For the Random
using System.Collections.Generic; // The List
// List:
List<ItemType> list = new List<ItemType>();
// Add x:
ItemType x = ...; // The item to insert into the list
list.Add( x );
// Random selection
Random r = ...; // Probably get this from somewhere else
int index = r.Next( list.Count );
ItemType y = list[index];
// Remove item at index
list[index] = list[list.Count - 1]; // Copy last item to index
list.RemoveAt( list.Count - 1 ); // Remove from end of list
编辑:当然,要删除List<ItemType>
中的元素,您需要知道其索引。如果要删除随机元素,可以使用随机索引(如上例所示)。如果要删除给定项目,可以保留Dictionary<ItemType,int>
将项目映射到其索引。添加,删除和更新这些索引都可以在O(1)(摊销)中完成。
这会导致所有操作的O(1)(摊销)的复杂性。